├── .gitignore ├── .rspec ├── Gemfile ├── Gemfile.lock ├── README.md ├── Rakefile ├── app ├── assets │ ├── images │ │ └── rails.png │ ├── javascripts │ │ ├── application.js │ │ ├── sessions.js.coffee │ │ ├── static_pages.js.coffee │ │ └── users.js.coffee │ └── stylesheets │ │ ├── application.css │ │ ├── custom.css.scss │ │ ├── sessions.css.scss │ │ ├── static_pages.css.scss │ │ └── users.css.scss ├── controllers │ ├── application_controller.rb │ ├── microposts_controller.rb │ ├── relationships_controller.rb │ ├── sessions_controller.rb │ ├── static_pages_controller.rb │ └── users_controller.rb ├── helpers │ ├── application_helper.rb │ ├── sessions_helper.rb │ ├── static_pages_helper.rb │ └── users_helper.rb ├── mailers │ └── .gitkeep ├── models │ ├── .gitkeep │ ├── micropost.rb │ ├── relationship.rb │ └── user.rb └── views │ ├── layouts │ ├── _footer.html.erb │ ├── _header.html.erb │ ├── _shim.html.erb │ └── application.html.erb │ ├── microposts │ └── _micropost.html.erb │ ├── relationships │ ├── create.js.erb │ └── destroy.js.erb │ ├── sessions │ └── new.html.erb │ ├── shared │ ├── _error_messages.html.erb │ ├── _feed.html.erb │ ├── _feed_item.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 │ └── 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 ├── config.ru ├── config ├── application.rb ├── boot.rb ├── cucumber.yml ├── database.yml ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── initializers │ ├── backtrace_silencers.rb │ ├── inflections.rb │ ├── mime_types.rb │ ├── secret_token.rb │ ├── session_store.rb │ └── wrap_parameters.rb ├── locales │ └── en.yml └── routes.rb ├── db ├── migrate │ ├── 20120507002709_create_users.rb │ ├── 20120508002644_add_index_to_users_email.rb │ ├── 20120508004837_add_password_digest_to_users.rb │ ├── 20120514201046_add_remember_token_to_users.rb │ ├── 20120517030607_add_admin_to_users.rb │ ├── 20120519194108_create_microposts.rb │ └── 20120520230206_create_relationships.rb ├── schema.rb └── seeds.rb ├── doc └── README_FOR_APP ├── features ├── signing_in.feature ├── step_definitions │ └── authentication_steps.rb └── support │ └── env.rb ├── lib ├── assets │ └── .gitkeep └── tasks │ ├── .gitkeep │ ├── cucumber.rake │ └── sample_data.rake ├── log └── .gitkeep ├── public ├── 404.html ├── 422.html ├── 500.html ├── favicon.ico └── robots.txt ├── script ├── cucumber └── rails ├── spec ├── controllers │ └── relationships_controller_spec.rb ├── factories.rb ├── helpers │ └── application_helper_spec.rb ├── models │ ├── micropost_spec.rb │ ├── relationship_spec.rb │ └── user_spec.rb ├── requests │ ├── authentication_pages_spec.rb │ ├── micropost_pages_spec.rb │ ├── static_pages_spec.rb │ └── user_pages_spec.rb ├── spec_helper.rb └── support │ └── utilities.rb └── vendor ├── assets ├── javascripts │ └── .gitkeep └── stylesheets │ └── .gitkeep └── plugins └── .gitkeep /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-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 | 13 | # Ignore all logfiles and tempfiles. 14 | /log/*.log 15 | /tmp 16 | .DS_Store 17 | bundler_stubs/ -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --colour 2 | --drb 3 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'rails', '3.2.11' 4 | gem 'jquery-rails', '2.0.0' 5 | gem 'bootstrap-sass', '2.0.0' 6 | gem 'bcrypt-ruby', '3.0.1' 7 | gem 'faker', '1.0.1' 8 | gem 'will_paginate', '3.0.3' 9 | gem 'bootstrap-will_paginate', '0.0.5' 10 | 11 | group :development, :test do 12 | gem 'sqlite3', '1.3.5' 13 | gem 'rspec-rails', '2.9.0' 14 | gem 'guard-rspec', '0.5.5' 15 | end 16 | 17 | # Gems used only for assets and not required 18 | # in production environments by default. 19 | group :assets do 20 | gem 'sass-rails', '3.2.4' 21 | gem 'coffee-rails', '3.2.2' 22 | gem 'uglifier', '1.2.3' 23 | end 24 | 25 | group :test do 26 | gem 'capybara', '1.1.2' 27 | gem 'factory_girl_rails', '1.4.0' 28 | gem 'cucumber-rails', '1.2.1', require: false 29 | gem 'database_cleaner', '0.7.0' 30 | gem 'rb-fsevent', '0.4.3.1', :require => false 31 | gem 'growl', '1.0.3' 32 | gem 'guard-spork', '0.3.2' 33 | gem 'spork', '0.9.0' 34 | gem 'launchy', '2.1.0' 35 | end 36 | 37 | group :production do 38 | gem 'pg', '0.12.2' 39 | end -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | actionmailer (3.2.3) 5 | actionpack (= 3.2.3) 6 | mail (~> 2.4.4) 7 | actionpack (3.2.3) 8 | activemodel (= 3.2.3) 9 | activesupport (= 3.2.3) 10 | builder (~> 3.0.0) 11 | erubis (~> 2.7.0) 12 | journey (~> 1.0.1) 13 | rack (~> 1.4.0) 14 | rack-cache (~> 1.2) 15 | rack-test (~> 0.6.1) 16 | sprockets (~> 2.1.2) 17 | activemodel (3.2.3) 18 | activesupport (= 3.2.3) 19 | builder (~> 3.0.0) 20 | activerecord (3.2.3) 21 | activemodel (= 3.2.3) 22 | activesupport (= 3.2.3) 23 | arel (~> 3.0.2) 24 | tzinfo (~> 0.3.29) 25 | activeresource (3.2.3) 26 | activemodel (= 3.2.3) 27 | activesupport (= 3.2.3) 28 | activesupport (3.2.3) 29 | i18n (~> 0.6) 30 | multi_json (~> 1.0) 31 | addressable (2.2.7) 32 | arel (3.0.2) 33 | bcrypt-ruby (3.0.1) 34 | bootstrap-sass (2.0.0) 35 | bootstrap-will_paginate (0.0.5) 36 | will_paginate 37 | builder (3.0.0) 38 | capybara (1.1.2) 39 | mime-types (>= 1.16) 40 | nokogiri (>= 1.3.3) 41 | rack (>= 1.0.0) 42 | rack-test (>= 0.5.4) 43 | selenium-webdriver (~> 2.0) 44 | xpath (~> 0.1.4) 45 | childprocess (0.3.1) 46 | ffi (~> 1.0.6) 47 | coffee-rails (3.2.2) 48 | coffee-script (>= 2.2.0) 49 | railties (~> 3.2.0) 50 | coffee-script (2.2.0) 51 | coffee-script-source 52 | execjs 53 | coffee-script-source (1.2.0) 54 | cucumber (1.1.9) 55 | builder (>= 2.1.2) 56 | diff-lcs (>= 1.1.2) 57 | gherkin (~> 2.9.0) 58 | json (>= 1.4.6) 59 | term-ansicolor (>= 1.0.6) 60 | cucumber-rails (1.2.1) 61 | capybara (>= 1.1.2) 62 | cucumber (>= 1.1.3) 63 | nokogiri (>= 1.5.0) 64 | database_cleaner (0.7.0) 65 | diff-lcs (1.1.3) 66 | erubis (2.7.0) 67 | execjs (1.3.0) 68 | multi_json (~> 1.0) 69 | factory_girl (2.3.2) 70 | activesupport 71 | factory_girl_rails (1.4.0) 72 | factory_girl (~> 2.3.0) 73 | railties (>= 3.0.0) 74 | faker (1.0.1) 75 | i18n (~> 0.4) 76 | ffi (1.0.11) 77 | gherkin (2.9.3) 78 | json (>= 1.4.6) 79 | growl (1.0.3) 80 | guard (1.0.1) 81 | ffi (>= 0.5.0) 82 | thor (~> 0.14.6) 83 | guard-rspec (0.5.5) 84 | guard (>= 0.8.4) 85 | guard-spork (0.3.2) 86 | guard (>= 0.8.4) 87 | spork (>= 0.8.4) 88 | hike (1.2.1) 89 | i18n (0.6.0) 90 | journey (1.0.3) 91 | jquery-rails (2.0.0) 92 | railties (>= 3.2.0.beta, < 5.0) 93 | thor (~> 0.14) 94 | json (1.6.6) 95 | launchy (2.1.0) 96 | addressable (~> 2.2.6) 97 | mail (2.4.4) 98 | i18n (>= 0.4.0) 99 | mime-types (~> 1.16) 100 | treetop (~> 1.4.8) 101 | mime-types (1.18) 102 | multi_json (1.2.0) 103 | nokogiri (1.5.2) 104 | pg (0.12.2) 105 | polyglot (0.3.3) 106 | rack (1.4.1) 107 | rack-cache (1.2) 108 | rack (>= 0.4) 109 | rack-ssl (1.3.2) 110 | rack 111 | rack-test (0.6.1) 112 | rack (>= 1.0) 113 | rails (3.2.3) 114 | actionmailer (= 3.2.3) 115 | actionpack (= 3.2.3) 116 | activerecord (= 3.2.3) 117 | activeresource (= 3.2.3) 118 | activesupport (= 3.2.3) 119 | bundler (~> 1.0) 120 | railties (= 3.2.3) 121 | railties (3.2.3) 122 | actionpack (= 3.2.3) 123 | activesupport (= 3.2.3) 124 | rack-ssl (~> 1.3.2) 125 | rake (>= 0.8.7) 126 | rdoc (~> 3.4) 127 | thor (~> 0.14.6) 128 | rake (0.9.2.2) 129 | rb-fsevent (0.4.3.1) 130 | rdoc (3.12) 131 | json (~> 1.4) 132 | rspec (2.9.0) 133 | rspec-core (~> 2.9.0) 134 | rspec-expectations (~> 2.9.0) 135 | rspec-mocks (~> 2.9.0) 136 | rspec-core (2.9.0) 137 | rspec-expectations (2.9.1) 138 | diff-lcs (~> 1.1.3) 139 | rspec-mocks (2.9.0) 140 | rspec-rails (2.9.0) 141 | actionpack (>= 3.0) 142 | activesupport (>= 3.0) 143 | railties (>= 3.0) 144 | rspec (~> 2.9.0) 145 | rubyzip (0.9.6.1) 146 | sass (3.1.15) 147 | sass-rails (3.2.4) 148 | railties (~> 3.2.0) 149 | sass (>= 3.1.10) 150 | tilt (~> 1.3) 151 | selenium-webdriver (2.20.0) 152 | childprocess (>= 0.2.5) 153 | ffi (~> 1.0) 154 | multi_json (~> 1.0) 155 | rubyzip 156 | spork (0.9.0) 157 | sprockets (2.1.2) 158 | hike (~> 1.2) 159 | rack (~> 1.0) 160 | tilt (~> 1.1, != 1.3.0) 161 | sqlite3 (1.3.5) 162 | term-ansicolor (1.0.7) 163 | thor (0.14.6) 164 | tilt (1.3.3) 165 | treetop (1.4.10) 166 | polyglot 167 | polyglot (>= 0.3.1) 168 | tzinfo (0.3.32) 169 | uglifier (1.2.3) 170 | execjs (>= 0.3.0) 171 | multi_json (>= 1.0.2) 172 | will_paginate (3.0.3) 173 | xpath (0.1.4) 174 | nokogiri (~> 1.3) 175 | 176 | PLATFORMS 177 | ruby 178 | 179 | DEPENDENCIES 180 | bcrypt-ruby (= 3.0.1) 181 | bootstrap-sass (= 2.0.0) 182 | bootstrap-will_paginate (= 0.0.5) 183 | capybara (= 1.1.2) 184 | coffee-rails (= 3.2.2) 185 | cucumber-rails (= 1.2.1) 186 | database_cleaner (= 0.7.0) 187 | factory_girl_rails (= 1.4.0) 188 | faker (= 1.0.1) 189 | growl (= 1.0.3) 190 | guard-rspec (= 0.5.5) 191 | guard-spork (= 0.3.2) 192 | jquery-rails (= 2.0.0) 193 | launchy (= 2.1.0) 194 | pg (= 0.12.2) 195 | rails (= 3.2.3) 196 | rb-fsevent (= 0.4.3.1) 197 | rspec-rails (= 2.9.0) 198 | sass-rails (= 3.2.4) 199 | spork (= 0.9.0) 200 | sqlite3 (= 1.3.5) 201 | uglifier (= 1.2.3) 202 | will_paginate (= 3.0.3) 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ruby on Rails Tutorial: sample app 2 | 3 | This is the sample application for the [Ruby on Rails Tutorial](http://railstutorial.org/) by [Michael Hartl](http://michaelhartl.com/). 4 | 5 | **NOTE**: This application is out of date. For the most up-to-date version of the Rails Tutorial sample application, see the [Rails Tutorial Help page](http://railstutorial.org/help). 6 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | # Add your own tasks in files placed in lib/tasks ending in .rake, 3 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 4 | 5 | require File.expand_path('../config/application', __FILE__) 6 | 7 | SampleApp::Application.load_tasks 8 | -------------------------------------------------------------------------------- /app/assets/images/rails.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mhartl/sample_app_rails_3_2/4ec29458818683903ae1fed9a9647eb2d5e332f6/app/assets/images/rails.png -------------------------------------------------------------------------------- /app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into application.js, which will include all the files 2 | // listed below. 3 | // 4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, 5 | // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path. 6 | // 7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 8 | // the compiled file. 9 | // 10 | // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD 11 | // GO AFTER THE REQUIRES BELOW. 12 | // 13 | //= require jquery 14 | //= require jquery_ujs 15 | //= require bootstrap 16 | //= require_tree . 17 | -------------------------------------------------------------------------------- /app/assets/javascripts/sessions.js.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/static_pages.js.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/users.js.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ 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, vendor/assets/stylesheets, 6 | * or vendor/assets/stylesheets of plugins, if any, 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 top of the 9 | * compiled file, but it's generally better to create a new file per style scope. 10 | * 11 | *= require_self 12 | *= require_tree . 13 | */ 14 | -------------------------------------------------------------------------------- /app/assets/stylesheets/custom.css.scss: -------------------------------------------------------------------------------- 1 | @import "bootstrap"; 2 | 3 | /* mixins, variables, etc. */ 4 | 5 | $grayMediumLight: #eaeaea; 6 | 7 | @mixin box_sizing { 8 | -moz-box-sizing: border-box; 9 | -webkit-box-sizing: border-box; 10 | box-sizing: border-box; 11 | } 12 | 13 | /* universal */ 14 | 15 | html { 16 | overflow-y: scroll; 17 | } 18 | 19 | body { 20 | padding-top: 60px; 21 | } 22 | 23 | section { 24 | overflow: auto; 25 | } 26 | 27 | textarea { 28 | resize: vertical; 29 | } 30 | 31 | .center { 32 | text-align: center; 33 | h1 { 34 | margin-bottom: 10px; 35 | } 36 | } 37 | 38 | /* typography */ 39 | 40 | h1, h2, h3, h4, h5, h6 { 41 | line-height: 1; 42 | } 43 | 44 | h1 { 45 | font-size: 3em; 46 | letter-spacing: -2px; 47 | margin-bottom: 30px; 48 | text-align: center; 49 | } 50 | 51 | h2 { 52 | font-size: 1.7em; 53 | letter-spacing: -1px; 54 | margin-bottom: 30px; 55 | text-align: center; 56 | font-weight: normal; 57 | color: $grayLight; 58 | } 59 | 60 | p { 61 | font-size: 1.1em; 62 | line-height: 1.7em; 63 | } 64 | 65 | 66 | /* header */ 67 | 68 | #logo { 69 | float: left; 70 | margin-right: 10px; 71 | font-size: 1.7em; 72 | color: white; 73 | text-transform: uppercase; 74 | letter-spacing: -1px; 75 | padding-top: 9px; 76 | font-weight: bold; 77 | line-height: 1; 78 | &:hover { 79 | color: white; 80 | text-decoration: none; 81 | } 82 | } 83 | 84 | /* footer */ 85 | 86 | footer { 87 | margin-top: 45px; 88 | padding-top: 5px; 89 | border-top: 1px solid $grayMediumLight; 90 | color: $grayLight; 91 | a { 92 | color: $gray; 93 | &:hover { 94 | color: $grayDarker; 95 | } 96 | } 97 | small { 98 | float: left; 99 | } 100 | ul { 101 | float: right; 102 | list-style: none; 103 | li { 104 | float: left; 105 | margin-left: 10px; 106 | } 107 | } 108 | } 109 | 110 | /* miscellaneous */ 111 | 112 | .debug_dump { 113 | clear: both; 114 | float: left; 115 | width: 100%; 116 | margin-top: 45px; 117 | @include box_sizing; 118 | } 119 | 120 | /* sidebar */ 121 | 122 | aside { 123 | section { 124 | padding: 10px 0; 125 | border-top: 1px solid $grayLighter; 126 | &:first-child { 127 | border: 0; 128 | padding-top: 0; 129 | } 130 | span { 131 | display: block; 132 | margin-bottom: 3px; 133 | line-height: 1; 134 | } 135 | h1 { 136 | font-size: 1.6em; 137 | text-align: left; 138 | letter-spacing: -1px; 139 | margin-bottom: 3px; 140 | } 141 | } 142 | } 143 | 144 | .gravatar { 145 | float: left; 146 | margin-right: 10px; 147 | } 148 | 149 | .stats { 150 | overflow: auto; 151 | a { 152 | float: left; 153 | padding: 0 10px; 154 | border-left: 1px solid $grayLighter; 155 | color: gray; 156 | &:first-child { 157 | padding-left: 0; 158 | border: 0; 159 | } 160 | &:hover { 161 | text-decoration: none; 162 | color: $blue; 163 | } 164 | } 165 | strong { 166 | display: block; 167 | } 168 | } 169 | 170 | .user_avatars { 171 | overflow: auto; 172 | margin-top: 10px; 173 | .gravatar { 174 | margin: 1px 1px; 175 | } 176 | } 177 | 178 | /* forms */ 179 | 180 | input, textarea, select, .uneditable-input { 181 | border: 1px solid #bbb; 182 | width: 100%; 183 | height: auto; 184 | margin-bottom: 15px; 185 | @include box_sizing; 186 | } 187 | 188 | #error_explanation { 189 | color: #f00; 190 | ul { 191 | list-style: none; 192 | margin: 0 0 18px 0; 193 | } 194 | } 195 | 196 | .field_with_errors { 197 | @extend .control-group; 198 | @extend .error; 199 | } 200 | 201 | /* users index */ 202 | 203 | .users { 204 | list-style: none; 205 | margin: 0; 206 | li { 207 | overflow: auto; 208 | padding: 10px 0; 209 | border-top: 1px solid $grayLighter; 210 | &:last-child { 211 | border-bottom: 1px solid $grayLighter; 212 | } 213 | } 214 | } 215 | 216 | /* microposts */ 217 | 218 | .microposts { 219 | list-style: none; 220 | margin: 10px 0 0 0; 221 | 222 | li { 223 | padding: 10px 0; 224 | border-top: 1px solid #e8e8e8; 225 | } 226 | } 227 | .content { 228 | display: block; 229 | } 230 | .timestamp { 231 | color: $grayLight; 232 | } 233 | .gravatar { 234 | float: left; 235 | margin-right: 10px; 236 | } 237 | aside { 238 | textarea { 239 | height: 100px; 240 | margin-bottom: 5px; 241 | } 242 | } 243 | 244 | -------------------------------------------------------------------------------- /app/assets/stylesheets/sessions.css.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.css.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.css.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/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | protect_from_forgery 3 | include SessionsHelper 4 | end 5 | -------------------------------------------------------------------------------- /app/controllers/microposts_controller.rb: -------------------------------------------------------------------------------- 1 | class MicropostsController < ApplicationController 2 | before_filter :signed_in_user 3 | before_filter :correct_user, only: :destroy 4 | 5 | def create 6 | @micropost = current_user.microposts.build(params[:micropost]) 7 | if @micropost.save 8 | flash[:success] = "Micropost created!" 9 | redirect_to root_path 10 | else 11 | @feed_items = [] 12 | render 'static_pages/home' 13 | end 14 | end 15 | 16 | def destroy 17 | @micropost.destroy 18 | redirect_to root_path 19 | end 20 | 21 | private 22 | 23 | def correct_user 24 | @micropost = current_user.microposts.find_by_id(params[:id]) 25 | redirect_to root_path if @micropost.nil? 26 | end 27 | end -------------------------------------------------------------------------------- /app/controllers/relationships_controller.rb: -------------------------------------------------------------------------------- 1 | class RelationshipsController < ApplicationController 2 | before_filter :signed_in_user 3 | 4 | def create 5 | @user = User.find(params[:relationship][: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 -------------------------------------------------------------------------------- /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]) 8 | if user && user.authenticate(params[:session][:password]) 9 | sign_in user 10 | redirect_back_or user 11 | else 12 | flash.now[:error] = "Invalid email/password combination" 13 | render 'new' 14 | end 15 | end 16 | 17 | def destroy 18 | sign_out 19 | redirect_to root_path 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /app/controllers/static_pages_controller.rb: -------------------------------------------------------------------------------- 1 | class StaticPagesController < ApplicationController 2 | def home 3 | if signed_in? 4 | @micropost = current_user.microposts.build 5 | @feed_items = current_user.feed.paginate(page: params[:page]) 6 | end 7 | end 8 | 9 | def help 10 | end 11 | 12 | def about 13 | end 14 | 15 | def contact 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/controllers/users_controller.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | before_filter :signed_in_user, 3 | only: [:index, :edit, :update, :destroy, :following, :followers] 4 | before_filter :correct_user, only: [:edit, :update] 5 | before_filter :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(params[:user]) 22 | if @user.save 23 | sign_in @user 24 | flash[:success] = "Welcome to the Sample App!" 25 | redirect_to @user 26 | else 27 | render 'new' 28 | end 29 | end 30 | 31 | def edit 32 | end 33 | 34 | def update 35 | if @user.update_attributes(params[:user]) 36 | sign_in @user 37 | flash[:success] = "Profile updated" 38 | redirect_to @user 39 | else 40 | render 'edit' 41 | end 42 | end 43 | 44 | def destroy 45 | User.find(params[:id]).destroy 46 | flash[:success] = "User destroyed" 47 | redirect_to users_path 48 | end 49 | 50 | def following 51 | @title = "Following" 52 | @user = User.find(params[:id]) 53 | @users = @user.followed_users.paginate(page: params[:page]) 54 | render 'show_follow' 55 | end 56 | 57 | def followers 58 | @title = "Followers" 59 | @user = User.find(params[:id]) 60 | @users = @user.followers.paginate(page: params[:page]) 61 | render 'show_follow' 62 | end 63 | 64 | private 65 | 66 | def correct_user 67 | @user = User.find(params[:id]) 68 | redirect_to root_path unless current_user?(@user) 69 | end 70 | 71 | def admin_user 72 | redirect_to root_path unless current_user.admin? 73 | end 74 | end 75 | -------------------------------------------------------------------------------- /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 | "#{base_title} | #{page_title}" 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/helpers/sessions_helper.rb: -------------------------------------------------------------------------------- 1 | module SessionsHelper 2 | 3 | def sign_in(user) 4 | cookies.permanent[:remember_token] = user.remember_token 5 | current_user = user 6 | end 7 | 8 | def signed_in? 9 | !current_user.nil? 10 | end 11 | 12 | def current_user=(user) 13 | @current_user = user 14 | end 15 | 16 | def current_user 17 | @current_user ||= User.find_by_remember_token(cookies[:remember_token]) 18 | end 19 | 20 | def current_user?(user) 21 | user == current_user 22 | end 23 | 24 | def sign_out 25 | current_user = nil 26 | cookies.delete(:remember_token) 27 | end 28 | 29 | def store_location 30 | session[:return_to] = request.fullpath 31 | end 32 | 33 | def redirect_back_or(default) 34 | redirect_to(session[:return_to] || default) 35 | session.delete(:return_to) 36 | end 37 | 38 | def signed_in_user 39 | unless signed_in? 40 | store_location 41 | redirect_to signin_path, notice: "Please sign in." 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /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: 50 }) 5 | gravatar_id = Digest::MD5::hexdigest(user.email.downcase) 6 | size = options[:size] 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/mailers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mhartl/sample_app_rails_3_2/4ec29458818683903ae1fed9a9647eb2d5e332f6/app/mailers/.gitkeep -------------------------------------------------------------------------------- /app/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mhartl/sample_app_rails_3_2/4ec29458818683903ae1fed9a9647eb2d5e332f6/app/models/.gitkeep -------------------------------------------------------------------------------- /app/models/micropost.rb: -------------------------------------------------------------------------------- 1 | class Micropost < ActiveRecord::Base 2 | attr_accessible :content 3 | belongs_to :user 4 | 5 | validates :user_id, presence: true 6 | validates :content, presence: true, length: { maximum: 140 } 7 | 8 | default_scope order: 'microposts.created_at DESC' 9 | 10 | def self.from_users_followed_by(user) 11 | followed_user_ids = "SELECT followed_id FROM relationships 12 | WHERE follower_id = :user_id" 13 | where("user_id IN (#{followed_user_ids}) OR user_id = :user_id", 14 | user_id: user.id) 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /app/models/relationship.rb: -------------------------------------------------------------------------------- 1 | class Relationship < ActiveRecord::Base 2 | attr_accessible :followed_id 3 | 4 | belongs_to :follower, class_name: "User" 5 | belongs_to :followed, class_name: "User" 6 | 7 | validates :follower_id, presence: true 8 | validates :followed_id, presence: true 9 | end 10 | -------------------------------------------------------------------------------- /app/models/user.rb: -------------------------------------------------------------------------------- 1 | class User < ActiveRecord::Base 2 | attr_accessible :name, :email, :password, :password_confirmation 3 | has_secure_password 4 | has_many :microposts, dependent: :destroy 5 | has_many :relationships, foreign_key: "follower_id", dependent: :destroy 6 | has_many :followed_users, through: :relationships, source: :followed 7 | has_many :reverse_relationships, foreign_key: "followed_id", 8 | class_name: "Relationship", 9 | dependent: :destroy 10 | has_many :followers, through: :reverse_relationships, source: :follower 11 | 12 | before_save { |user| user.email = user.email.downcase } 13 | before_save :create_remember_token 14 | 15 | validates :name, presence: true, length: { maximum: 50 } 16 | VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i 17 | validates :email, presence: true, format: { with: VALID_EMAIL_REGEX }, 18 | uniqueness: { case_sensitive: false } 19 | validates :password, length: { minimum: 6 } 20 | validates :password_confirmation, presence: true 21 | 22 | def following?(other_user) 23 | relationships.find_by_followed_id(other_user.id) 24 | end 25 | 26 | def follow!(other_user) 27 | relationships.create!(followed_id: other_user.id) 28 | end 29 | 30 | def unfollow!(other_user) 31 | relationships.find_by_followed_id(other_user.id).destroy 32 | end 33 | 34 | def feed 35 | Micropost.from_users_followed_by(self) 36 | end 37 | 38 | private 39 | 40 | def create_remember_token 41 | self.remember_token = SecureRandom.urlsafe_base64 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /app/views/layouts/_footer.html.erb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/views/layouts/_header.html.erb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/views/layouts/_shim.html.erb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= full_title(yield(:title)) %> 5 | <%= stylesheet_link_tag "application", media: "all" %> 6 | <%= javascript_include_tag "application" %> 7 | <%= csrf_meta_tags %> 8 | <%= render 'layouts/shim' %> 9 | 10 | 11 | <%= render 'layouts/header' %> 12 |
13 | <% flash.each do |key, value| %> 14 | <%= content_tag(:div, value, class: "alert alert-#{key}") %> 15 | <% end %> 16 | <%= yield %> 17 | <%= render 'layouts/footer' %> 18 | <%= debug(params) if Rails.env.development? %> 19 |
20 | 21 | 22 | -------------------------------------------------------------------------------- /app/views/microposts/_micropost.html.erb: -------------------------------------------------------------------------------- 1 |
  • 2 | <%= micropost.content %> 3 | 4 | Posted <%= time_ago_in_words(micropost.created_at) %> ago. 5 | 6 | <% if current_user?(micropost.user) %> 7 | <%= link_to "delete", micropost, method: :delete, 8 | confirm: "You sure?", 9 | title: micropost.content %> 10 | <% end %> 11 |
  • -------------------------------------------------------------------------------- /app/views/relationships/create.js.erb: -------------------------------------------------------------------------------- 1 | $("#follow_form").html("<%= escape_javascript(render('users/unfollow')) %>") 2 | $("#followers").html('<%= @user.followers.count %>') -------------------------------------------------------------------------------- /app/views/relationships/destroy.js.erb: -------------------------------------------------------------------------------- 1 | $("#follow_form").html("<%= escape_javascript(render('users/follow')) %>") 2 | $("#followers").html('<%= @user.followers.count %>') -------------------------------------------------------------------------------- /app/views/sessions/new.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, 'Sign in') %> 2 |

    Sign in

    3 | 4 |
    5 |
    6 | <%= form_for(:session, url: sessions_path) do |f| %> 7 | 8 | <%= f.label :email %> 9 | <%= f.text_field :email %> 10 | 11 | <%= f.label :password %> 12 | <%= f.password_field :password %> 13 | 14 | <%= f.submit "Sign in", class: "btn btn-large btn-primary" %> 15 | <% end %> 16 | 17 |

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

    18 |
    19 |
    -------------------------------------------------------------------------------- /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 | 11 |
    12 | <% end %> -------------------------------------------------------------------------------- /app/views/shared/_feed.html.erb: -------------------------------------------------------------------------------- 1 | <% if @feed_items.any? %> 2 |
      3 | <%= render partial: 'shared/feed_item', collection: @feed_items %> 4 |
    5 | <%= will_paginate @feed_items %> 6 | <% end %> -------------------------------------------------------------------------------- /app/views/shared/_feed_item.html.erb: -------------------------------------------------------------------------------- 1 |
  • 2 | <%= link_to gravatar_for(feed_item.user), feed_item.user %> 3 | 4 | <%= link_to feed_item.user.name, feed_item.user %> 5 | 6 | <%= feed_item.content %> 7 | 8 | Posted <%= time_ago_in_words(feed_item.created_at) %> ago. 9 | 10 | <% if current_user?(feed_item.user) %> 11 | <%= link_to "delete", feed_item, method: :delete, 12 | confirm: "You sure?", 13 | title: feed_item.content %> 14 | <% end %> 15 |
  • -------------------------------------------------------------------------------- /app/views/shared/_micropost_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_for(@micropost) do |f| %> 2 | <%= render 'shared/error_messages', object: @micropost %> 3 |
    4 | <%= f.text_area :content, placeholder: "Compose new micropost..." %> 5 |
    6 | <%= f.submit "Post", class: "btn btn-large btn-primary" %> 7 | <% end %> -------------------------------------------------------------------------------- /app/views/shared/_stats.html.erb: -------------------------------------------------------------------------------- 1 | <% @user ||= current_user %> 2 | 3 |
    4 | 5 | 6 | <%= @user.followed_users.count %> 7 | 8 | following 9 | 10 | 11 | 12 | <%= @user.followers.count %> 13 | 14 | followers 15 | 16 |
    -------------------------------------------------------------------------------- /app/views/shared/_user_info.html.erb: -------------------------------------------------------------------------------- 1 | 2 | <%= gravatar_for current_user, size: 52 %> 3 | 4 |

    <%= current_user.name %>

    5 | <%= link_to "view my profile", current_user %> 6 | <%= pluralize(current_user.microposts.count, "micropost") %> -------------------------------------------------------------------------------- /app/views/static_pages/about.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, 'About Us') %> 2 |

    About Us

    3 |

    4 | The Ruby on Rails Tutorial is a project to make a book and screencasts to teach web development with 5 | Ruby on Rails. This is the sample 6 | application for the tutorial. 7 |

    -------------------------------------------------------------------------------- /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 |

    -------------------------------------------------------------------------------- /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 | Rails Tutorial book. 8 |

    9 | -------------------------------------------------------------------------------- /app/views/static_pages/home.html.erb: -------------------------------------------------------------------------------- 1 | <% if signed_in? %> 2 |
    3 | 14 |
    15 |

    Micropost Feed

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

    Welcome to the Sample App

    22 |

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

    27 | 28 | <%= link_to "Sign up now!", signup_path, class: "btn btn-large btn-primary" %> 29 |
    30 | 31 | <%= link_to image_tag("rails.png", alt: "Rails"), 'http://rubyonrails.org/' %> 32 | <% end %> -------------------------------------------------------------------------------- /app/views/users/_follow.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_for(current_user.relationships.build(followed_id: @user.id), 2 | remote: true) do |f| %> 3 |
    <%= f.hidden_field :followed_id %>
    4 | <%= f.submit "Follow", class: "btn btn-large btn-primary" %> 5 | <% end %> -------------------------------------------------------------------------------- /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 %> -------------------------------------------------------------------------------- /app/views/users/_unfollow.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_for(current_user.relationships.find_by_followed_id(@user), 2 | html: { method: :delete }, 3 | remote: true) do |f| %> 4 | <%= f.submit "Unfollow", class: "btn btn-large" %> 5 | <% end %> -------------------------------------------------------------------------------- /app/views/users/_user.html.erb: -------------------------------------------------------------------------------- 1 |
  • 2 | <%= gravatar_for user, size: 52 %> 3 | <%= link_to user.name, user %> 4 | <% if current_user.admin? && !current_user?(user) %> 5 | | <%= link_to "delete", user, method: :delete, confirm: "You sure?" %> 6 | <% end %> 7 |
  • -------------------------------------------------------------------------------- /app/views/users/edit.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, 'Edit user') %> 2 |

    Update your profile

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

    All users

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

    Sign up

    3 | 4 |
    5 |
    6 | <%= form_for(@user) do |f| %> 7 | <%= render 'shared/error_messages', object: @user %> 8 | <%= f.label :name %> 9 | <%= f.text_field :name %> 10 | 11 | <%= f.label :email %> 12 | <%= f.text_field :email %> 13 | 14 | <%= f.label :password %> 15 | <%= f.password_field :password %> 16 | 17 | <%= f.label :password_confirmation, "Confirmation" %> 18 | <%= f.password_field :password_confirmation %> 19 | 20 | <%= f.submit "Create my account", class: "btn btn-large btn-primary" %> 21 | <% end %> 22 |
    23 |
    -------------------------------------------------------------------------------- /app/views/users/show.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, @user.name) %> 2 |
    3 | 14 |
    15 | <%= render 'follow_form' if signed_in? %> 16 | <% if @user.microposts.any? %> 17 |

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

    18 |
      19 | <%= render @microposts %> 20 |
    21 | 22 | <%= will_paginate @microposts %> 23 | <% end %> 24 |
    25 |
    -------------------------------------------------------------------------------- /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 |
    -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require ::File.expand_path('../config/environment', __FILE__) 4 | run SampleApp::Application 5 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../boot', __FILE__) 2 | 3 | # Pick the frameworks you want: 4 | require "active_record/railtie" 5 | require "action_controller/railtie" 6 | require "action_mailer/railtie" 7 | require "active_resource/railtie" 8 | require "sprockets/railtie" 9 | # require "rails/test_unit/railtie" 10 | 11 | if defined?(Bundler) 12 | # If you precompile assets before deploying to production, use this line 13 | Bundler.require(*Rails.groups(:assets => %w(development test))) 14 | # If you want your assets lazily compiled in production, use this line 15 | # Bundler.require(:default, :assets, Rails.env) 16 | end 17 | 18 | module SampleApp 19 | class Application < Rails::Application 20 | # Settings in config/environments/* take precedence over those specified here. 21 | # Application configuration should go into files in config/initializers 22 | # -- all .rb files in that directory are automatically loaded. 23 | 24 | # Custom directories with classes and modules you want to be autoloadable. 25 | # config.autoload_paths += %W(#{config.root}/extras) 26 | 27 | # Only load the plugins named here, in the order given (default is alphabetical). 28 | # :all can be used as a placeholder for all plugins not explicitly named. 29 | # config.plugins = [ :exception_notification, :ssl_requirement, :all ] 30 | 31 | # Activate observers that should always be running. 32 | # config.active_record.observers = :cacher, :garbage_collector, :forum_observer 33 | 34 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 35 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 36 | # config.time_zone = 'Central Time (US & Canada)' 37 | 38 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. 39 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] 40 | # config.i18n.default_locale = :de 41 | 42 | # Configure the default encoding used in templates for Ruby 1.9. 43 | config.encoding = "utf-8" 44 | 45 | # Configure sensitive parameters which will be filtered from the log file. 46 | config.filter_parameters += [:password] 47 | 48 | # Use SQL instead of Active Record's schema dumper when creating the database. 49 | # This is necessary if your schema can't be completely dumped by the schema dumper, 50 | # like if you have constraints or database-specific column types 51 | # config.active_record.schema_format = :sql 52 | 53 | # Enforce whitelist mode for mass assignment. 54 | # This will create an empty whitelist of attributes available for mass-assignment for all models 55 | # in your app. As such, your models will need to explicitly whitelist or blacklist accessible 56 | # parameters by using an attr_accessible or attr_protected declaration. 57 | config.active_record.whitelist_attributes = true 58 | 59 | # Enable the asset pipeline 60 | config.assets.enabled = true 61 | 62 | # Version of your assets, change this if you want to expire all your assets 63 | config.assets.version = '1.0' 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | 3 | # Set up gems listed in the Gemfile. 4 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 5 | 6 | require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE']) 7 | -------------------------------------------------------------------------------- /config/cucumber.yml: -------------------------------------------------------------------------------- 1 | <% 2 | rerun = File.file?('rerun.txt') ? IO.read('rerun.txt') : "" 3 | rerun_opts = rerun.to_s.strip.empty? ? "--format #{ENV['CUCUMBER_FORMAT'] || 'progress'} features" : "--format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} #{rerun}" 4 | std_opts = "--format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} --strict --tags ~@wip" 5 | %> 6 | default: <%= std_opts %> features 7 | wip: --tags @wip:3 --wip features 8 | rerun: <%= rerun_opts %> --format rerun --out rerun.txt --strict --tags ~@wip 9 | -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite version 3.x 2 | # gem install sqlite3 3 | # 4 | # Ensure the SQLite 3 gem is defined in your Gemfile 5 | # gem 'sqlite3' 6 | development: 7 | adapter: sqlite3 8 | database: db/development.sqlite3 9 | pool: 5 10 | timeout: 5000 11 | 12 | # Warning: The database defined as "test" will be erased and 13 | # re-generated from your development database when you run "rake". 14 | # Do not set this db to the same as development or production. 15 | test: &test 16 | adapter: sqlite3 17 | database: db/test.sqlite3 18 | pool: 5 19 | timeout: 5000 20 | 21 | production: 22 | adapter: sqlite3 23 | database: db/production.sqlite3 24 | pool: 5 25 | timeout: 5000 26 | 27 | cucumber: 28 | <<: *test -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the rails application 2 | require File.expand_path('../application', __FILE__) 3 | 4 | # Initialize the rails application 5 | SampleApp::Application.initialize! 6 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | SampleApp::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 | # Log error messages when you accidentally call methods on nil. 10 | config.whiny_nils = true 11 | 12 | # Show full error reports and disable caching 13 | config.consider_all_requests_local = true 14 | config.action_controller.perform_caching = false 15 | 16 | # Don't care if the mailer can't send 17 | config.action_mailer.raise_delivery_errors = false 18 | 19 | # Print deprecation notices to the Rails logger 20 | config.active_support.deprecation = :log 21 | 22 | # Only use best-standards-support built into browsers 23 | config.action_dispatch.best_standards_support = :builtin 24 | 25 | # Raise exception on mass assignment protection for Active Record models 26 | config.active_record.mass_assignment_sanitizer = :strict 27 | 28 | # Log the query plan for queries taking more than this (works 29 | # with SQLite, MySQL, and PostgreSQL) 30 | config.active_record.auto_explain_threshold_in_seconds = 0.5 31 | 32 | # Do not compress assets 33 | config.assets.compress = false 34 | 35 | # Expands the lines which load the assets 36 | config.assets.debug = true 37 | end 38 | -------------------------------------------------------------------------------- /config/environments/production.rb: -------------------------------------------------------------------------------- 1 | SampleApp::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 | # Full error reports are disabled and caching is turned on 8 | config.consider_all_requests_local = false 9 | config.action_controller.perform_caching = true 10 | 11 | # Disable Rails's static asset server (Apache or nginx will already do this) 12 | config.serve_static_assets = false 13 | 14 | # Compress JavaScripts and CSS 15 | config.assets.compress = true 16 | 17 | # Don't fallback to assets pipeline if a precompiled asset is missed 18 | config.assets.compile = false 19 | 20 | # Generate digests for assets URLs 21 | config.assets.digest = true 22 | 23 | # Defaults to Rails.root.join("public/assets") 24 | # config.assets.manifest = YOUR_PATH 25 | 26 | # Specifies the header that your server uses for sending files 27 | # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache 28 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx 29 | 30 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 31 | config.force_ssl = true 32 | 33 | # See everything in the log (default is :info) 34 | # config.log_level = :debug 35 | 36 | # Prepend all log lines with the following tags 37 | # config.log_tags = [ :subdomain, :uuid ] 38 | 39 | # Use a different logger for distributed setups 40 | # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) 41 | 42 | # Use a different cache store in production 43 | # config.cache_store = :mem_cache_store 44 | 45 | # Enable serving of images, stylesheets, and JavaScripts from an asset server 46 | # config.action_controller.asset_host = "http://assets.example.com" 47 | 48 | # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added) 49 | # config.assets.precompile += %w( search.js ) 50 | 51 | # Disable delivery errors, bad email addresses will be ignored 52 | # config.action_mailer.raise_delivery_errors = false 53 | 54 | # Enable threaded mode 55 | # config.threadsafe! 56 | 57 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 58 | # the I18n.default_locale when a translation can not be found) 59 | config.i18n.fallbacks = true 60 | 61 | # Send deprecation notices to registered listeners 62 | config.active_support.deprecation = :notify 63 | 64 | # Log the query plan for queries taking more than this (works 65 | # with SQLite, MySQL, and PostgreSQL) 66 | # config.active_record.auto_explain_threshold_in_seconds = 0.5 67 | end 68 | -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | SampleApp::Application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Configure static asset server for tests with Cache-Control for performance 11 | config.serve_static_assets = true 12 | config.static_cache_control = "public, max-age=3600" 13 | 14 | # Log error messages when you accidentally call methods on nil 15 | config.whiny_nils = true 16 | 17 | # Show full error reports and disable caching 18 | config.consider_all_requests_local = true 19 | config.action_controller.perform_caching = false 20 | 21 | # Raise exceptions instead of rendering exception templates 22 | config.action_dispatch.show_exceptions = false 23 | 24 | # Disable request forgery protection in test environment 25 | config.action_controller.allow_forgery_protection = false 26 | 27 | # Tell Action Mailer not to deliver emails to the real world. 28 | # The :test delivery method accumulates sent emails in the 29 | # ActionMailer::Base.deliveries array. 30 | config.action_mailer.delivery_method = :test 31 | 32 | # Raise exception on mass assignment protection for Active Record models 33 | config.active_record.mass_assignment_sanitizer = :strict 34 | 35 | # Print deprecation notices to the stderr 36 | config.active_support.deprecation = :stderr 37 | 38 | # Speed up tests by lowering BCrypt's cost function. 39 | require 'bcrypt' 40 | silence_warnings do 41 | BCrypt::Engine::DEFAULT_COST = BCrypt::Engine::MIN_COST 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /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/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format 4 | # (all these examples are active by default): 5 | # ActiveSupport::Inflector.inflections do |inflect| 6 | # inflect.plural /^(ox)$/i, '\1en' 7 | # inflect.singular /^(ox)en/i, '\1' 8 | # inflect.irregular 'person', 'people' 9 | # inflect.uncountable %w( fish sheep ) 10 | # end 11 | # 12 | # These inflection rules are supported but not enabled by default: 13 | # ActiveSupport::Inflector.inflections do |inflect| 14 | # inflect.acronym 'RESTful' 15 | # end 16 | -------------------------------------------------------------------------------- /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 | # Mime::Type.register_alias "text/html", :iphone 6 | -------------------------------------------------------------------------------- /config/initializers/secret_token.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | # Make sure the secret is at least 30 characters and all random, 6 | # no regular words or you'll be exposed to dictionary attacks. 7 | SampleApp::Application.config.secret_token = '686c59432a66ac820f0dd9398227014724abe61c1501c67f869c9a6b3c3796e3b0a7ba15fb48d1dfa438348bcd1aa89ccd94fae87cdf1a8698e03395ff832392' 8 | -------------------------------------------------------------------------------- /config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | SampleApp::Application.config.session_store :cookie_store, key: '_sample_app_session' 4 | 5 | # Use the database for sessions instead of the cookie-based default, 6 | # which shouldn't be used to store highly confidential information 7 | # (create the session table with "rails generate session_migration") 8 | # SampleApp::Application.config.session_store :active_record_store 9 | -------------------------------------------------------------------------------- /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 | # Disable root element in JSON by default. 12 | ActiveSupport.on_load(:active_record) do 13 | self.include_root_in_json = false 14 | end 15 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Sample localization file for English. Add more files in this directory for other locales. 2 | # See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points. 3 | 4 | en: 5 | activerecord: 6 | attributes: 7 | user: 8 | password_digest: "Password" -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | SampleApp::Application.routes.draw do 2 | resources :users do 3 | member do 4 | get :following, :followers 5 | end 6 | end 7 | resources :sessions, only: [:new, :create, :destroy] 8 | resources :microposts, only: [:create, :destroy] 9 | resources :relationships, only: [:create, :destroy] 10 | 11 | root to: 'static_pages#home' 12 | 13 | match '/signup', to: 'users#new' 14 | match '/signin', to: 'sessions#new' 15 | match '/signout', to: 'sessions#destroy', via: :delete 16 | 17 | match '/help', to: 'static_pages#help' 18 | match '/about', to: 'static_pages#about' 19 | match '/contact', to: 'static_pages#contact' 20 | 21 | # The priority is based upon order of creation: 22 | # first created -> highest priority. 23 | 24 | # Sample of regular route: 25 | # match 'products/:id' => 'catalog#view' 26 | # Keep in mind you can assign values other than :controller and :action 27 | 28 | # Sample of named route: 29 | # match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase 30 | # This route can be invoked with purchase_url(:id => product.id) 31 | 32 | # Sample resource route (maps HTTP verbs to controller actions automatically): 33 | # resources :products 34 | 35 | # Sample resource route with options: 36 | # resources :products do 37 | # member do 38 | # get 'short' 39 | # post 'toggle' 40 | # end 41 | # 42 | # collection do 43 | # get 'sold' 44 | # end 45 | # end 46 | 47 | # Sample resource route with sub-resources: 48 | # resources :products do 49 | # resources :comments, :sales 50 | # resource :seller 51 | # end 52 | 53 | # Sample resource route with more complex sub-resources 54 | # resources :products do 55 | # resources :comments 56 | # resources :sales do 57 | # get 'recent', :on => :collection 58 | # end 59 | # end 60 | 61 | # Sample resource route within a namespace: 62 | # namespace :admin do 63 | # # Directs /admin/products/* to Admin::ProductsController 64 | # # (app/controllers/admin/products_controller.rb) 65 | # resources :products 66 | # end 67 | 68 | # You can have the root of your site routed with "root" 69 | # just remember to delete public/index.html. 70 | # root :to => 'welcome#index' 71 | 72 | # See how all your routes lay out with "rake routes" 73 | 74 | # This is a legacy wild controller route that's not recommended for RESTful applications. 75 | # Note: This route will make all actions in every controller accessible via GET requests. 76 | # match ':controller(/:action(/:id))(.:format)' 77 | end 78 | -------------------------------------------------------------------------------- /db/migrate/20120507002709_create_users.rb: -------------------------------------------------------------------------------- 1 | class CreateUsers < ActiveRecord::Migration 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/20120508002644_add_index_to_users_email.rb: -------------------------------------------------------------------------------- 1 | class AddIndexToUsersEmail < ActiveRecord::Migration 2 | def change 3 | add_index :users, :email, unique: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20120508004837_add_password_digest_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddPasswordDigestToUsers < ActiveRecord::Migration 2 | def change 3 | add_column :users, :password_digest, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20120514201046_add_remember_token_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddRememberTokenToUsers < ActiveRecord::Migration 2 | def change 3 | add_column :users, :remember_token, :string 4 | add_index :users, :remember_token 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20120517030607_add_admin_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddAdminToUsers < ActiveRecord::Migration 2 | def change 3 | add_column :users, :admin, :boolean, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20120519194108_create_microposts.rb: -------------------------------------------------------------------------------- 1 | class CreateMicroposts < ActiveRecord::Migration 2 | def change 3 | create_table :microposts do |t| 4 | t.string :content 5 | t.integer :user_id 6 | 7 | t.timestamps 8 | end 9 | add_index :microposts, [:user_id, :created_at] 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20120520230206_create_relationships.rb: -------------------------------------------------------------------------------- 1 | class CreateRelationships < ActiveRecord::Migration 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 | 10 | add_index :relationships, :follower_id 11 | add_index :relationships, :followed_id 12 | add_index :relationships, [:follower_id, :followed_id], unique: true 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /db/schema.rb: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | # This file is auto-generated from the current state of the database. Instead 3 | # of editing this file, please use the migrations feature of Active Record to 4 | # incrementally modify your database, and then regenerate this schema definition. 5 | # 6 | # Note that this schema.rb definition is the authoritative source for your 7 | # database schema. If you need to create the application database on another 8 | # system, you should be using db:schema:load, not running all the migrations 9 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 10 | # you'll amass, the slower it'll run and the greater likelihood for issues). 11 | # 12 | # It's strongly recommended to check this file into your version control system. 13 | 14 | ActiveRecord::Schema.define(:version => 20120520230206) do 15 | 16 | create_table "microposts", :force => true do |t| 17 | t.string "content" 18 | t.integer "user_id" 19 | t.datetime "created_at", :null => false 20 | t.datetime "updated_at", :null => false 21 | end 22 | 23 | add_index "microposts", ["user_id", "created_at"], :name => "index_microposts_on_user_id_and_created_at" 24 | 25 | create_table "relationships", :force => true do |t| 26 | t.integer "follower_id" 27 | t.integer "followed_id" 28 | t.datetime "created_at", :null => false 29 | t.datetime "updated_at", :null => false 30 | end 31 | 32 | add_index "relationships", ["followed_id"], :name => "index_relationships_on_followed_id" 33 | add_index "relationships", ["follower_id", "followed_id"], :name => "index_relationships_on_follower_id_and_followed_id", :unique => true 34 | add_index "relationships", ["follower_id"], :name => "index_relationships_on_follower_id" 35 | 36 | create_table "users", :force => true do |t| 37 | t.string "name" 38 | t.string "email" 39 | t.datetime "created_at", :null => false 40 | t.datetime "updated_at", :null => false 41 | t.string "password_digest" 42 | t.string "remember_token" 43 | t.boolean "admin", :default => false 44 | end 45 | 46 | add_index "users", ["email"], :name => "index_users_on_email", :unique => true 47 | add_index "users", ["remember_token"], :name => "index_users_on_remember_token" 48 | 49 | end 50 | -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }]) 7 | # Mayor.create(name: 'Emanuel', city: cities.first) 8 | -------------------------------------------------------------------------------- /doc/README_FOR_APP: -------------------------------------------------------------------------------- 1 | Use this README file to introduce your application and point to useful places in the API for learning more. 2 | Run "rake doc:app" to generate API documentation for your models, controllers, helpers, and libraries. 3 | -------------------------------------------------------------------------------- /features/signing_in.feature: -------------------------------------------------------------------------------- 1 | Feature: Signing in 2 | 3 | Scenario: Unsuccessful signin 4 | Given a user visits the signin page 5 | When he submits invalid signin information 6 | Then he should see an error message 7 | 8 | Scenario: Successful signin 9 | Given a user visits the signin page 10 | And the user has an account 11 | And the user submits valid signin information 12 | Then he should see his profile page 13 | And he should see a signout link -------------------------------------------------------------------------------- /features/step_definitions/authentication_steps.rb: -------------------------------------------------------------------------------- 1 | Given /^a user visits the signin page$/ do 2 | visit signin_path 3 | end 4 | 5 | When /^he submits invalid signin information$/ do 6 | click_button "Sign in" 7 | end 8 | 9 | Then /^he should see an error message$/ do 10 | page.should have_selector('div.alert.alert-error') 11 | end 12 | 13 | Given /^the user has an account$/ do 14 | @user = User.create(name: "Example User", email: "user@example.com", 15 | password: "foobar", password_confirmation: "foobar") 16 | end 17 | 18 | Given /^the user submits valid signin information$/ do 19 | fill_in "Email", with: @user.email 20 | fill_in "Password", with: @user.password 21 | click_button "Sign in" 22 | end 23 | 24 | Then /^he should see his profile page$/ do 25 | page.should have_selector('title', text: @user.name) 26 | end 27 | 28 | Then /^he should see a signout link$/ do 29 | page.should have_link('Sign out', href: signout_path) 30 | end -------------------------------------------------------------------------------- /features/support/env.rb: -------------------------------------------------------------------------------- 1 | # IMPORTANT: This file is generated by cucumber-rails - edit at your own peril. 2 | # It is recommended to regenerate this file in the future when you upgrade to a 3 | # newer version of cucumber-rails. Consider adding your own code to a new file 4 | # instead of editing this one. Cucumber will automatically load all features/**/*.rb 5 | # files. 6 | 7 | require 'cucumber/rails' 8 | 9 | # Capybara defaults to XPath selectors rather than Webrat's default of CSS3. In 10 | # order to ease the transition to Capybara we set the default here. If you'd 11 | # prefer to use XPath just remove this line and adjust any selectors in your 12 | # steps to use the XPath syntax. 13 | Capybara.default_selector = :css 14 | 15 | # By default, any exception happening in your Rails application will bubble up 16 | # to Cucumber so that your scenario will fail. This is a different from how 17 | # your application behaves in the production environment, where an error page will 18 | # be rendered instead. 19 | # 20 | # Sometimes we want to override this default behaviour and allow Rails to rescue 21 | # exceptions and display an error page (just like when the app is running in production). 22 | # Typical scenarios where you want to do this is when you test your error pages. 23 | # There are two ways to allow Rails to rescue exceptions: 24 | # 25 | # 1) Tag your scenario (or feature) with @allow-rescue 26 | # 27 | # 2) Set the value below to true. Beware that doing this globally is not 28 | # recommended as it will mask a lot of errors for you! 29 | # 30 | ActionController::Base.allow_rescue = false 31 | 32 | # Remove/comment out the lines below if your app doesn't have a database. 33 | # For some databases (like MongoDB and CouchDB) you may need to use :truncation instead. 34 | begin 35 | DatabaseCleaner.strategy = :transaction 36 | rescue NameError 37 | raise "You need to add database_cleaner to your Gemfile (in the :test group) if you wish to use it." 38 | end 39 | 40 | # You may also want to configure DatabaseCleaner to use different strategies for certain features and scenarios. 41 | # See the DatabaseCleaner documentation for details. Example: 42 | # 43 | # Before('@no-txn,@selenium,@culerity,@celerity,@javascript') do 44 | # DatabaseCleaner.strategy = :truncation, {:except => %w[widgets]} 45 | # end 46 | # 47 | # Before('~@no-txn', '~@selenium', '~@culerity', '~@celerity', '~@javascript') do 48 | # DatabaseCleaner.strategy = :transaction 49 | # end 50 | # 51 | 52 | # Possible values are :truncation and :transaction 53 | # The :transaction strategy is faster, but might give you threading problems. 54 | # See https://github.com/cucumber/cucumber-rails/blob/master/features/choose_javascript_database_strategy.feature 55 | Cucumber::Rails::Database.javascript_strategy = :truncation 56 | 57 | -------------------------------------------------------------------------------- /lib/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mhartl/sample_app_rails_3_2/4ec29458818683903ae1fed9a9647eb2d5e332f6/lib/assets/.gitkeep -------------------------------------------------------------------------------- /lib/tasks/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mhartl/sample_app_rails_3_2/4ec29458818683903ae1fed9a9647eb2d5e332f6/lib/tasks/.gitkeep -------------------------------------------------------------------------------- /lib/tasks/cucumber.rake: -------------------------------------------------------------------------------- 1 | # IMPORTANT: This file is generated by cucumber-rails - edit at your own peril. 2 | # It is recommended to regenerate this file in the future when you upgrade to a 3 | # newer version of cucumber-rails. Consider adding your own code to a new file 4 | # instead of editing this one. Cucumber will automatically load all features/**/*.rb 5 | # files. 6 | 7 | 8 | unless ARGV.any? {|a| a =~ /^gems/} # Don't load anything when running the gems:* tasks 9 | 10 | vendored_cucumber_bin = Dir["#{Rails.root}/vendor/{gems,plugins}/cucumber*/bin/cucumber"].first 11 | $LOAD_PATH.unshift(File.dirname(vendored_cucumber_bin) + '/../lib') unless vendored_cucumber_bin.nil? 12 | 13 | begin 14 | require 'cucumber/rake/task' 15 | 16 | namespace :cucumber do 17 | Cucumber::Rake::Task.new({:ok => 'db:test:prepare'}, 'Run features that should pass') do |t| 18 | t.binary = vendored_cucumber_bin # If nil, the gem's binary is used. 19 | t.fork = true # You may get faster startup if you set this to false 20 | t.profile = 'default' 21 | end 22 | 23 | Cucumber::Rake::Task.new({:wip => 'db:test:prepare'}, 'Run features that are being worked on') do |t| 24 | t.binary = vendored_cucumber_bin 25 | t.fork = true # You may get faster startup if you set this to false 26 | t.profile = 'wip' 27 | end 28 | 29 | Cucumber::Rake::Task.new({:rerun => 'db:test:prepare'}, 'Record failing features and run only them if any exist') do |t| 30 | t.binary = vendored_cucumber_bin 31 | t.fork = true # You may get faster startup if you set this to false 32 | t.profile = 'rerun' 33 | end 34 | 35 | desc 'Run all features' 36 | task :all => [:ok, :wip] 37 | 38 | task :statsetup do 39 | require 'rails/code_statistics' 40 | ::STATS_DIRECTORIES << %w(Cucumber\ features features) if File.exist?('features') 41 | ::CodeStatistics::TEST_TYPES << "Cucumber features" if File.exist?('features') 42 | end 43 | end 44 | desc 'Alias for cucumber:ok' 45 | task :cucumber => 'cucumber:ok' 46 | 47 | task :default => :cucumber 48 | 49 | task :features => :cucumber do 50 | STDERR.puts "*** The 'features' task is deprecated. See rake -T cucumber ***" 51 | end 52 | 53 | # In case we don't have ActiveRecord, append a no-op task that we can depend upon. 54 | task 'db:test:prepare' do 55 | end 56 | 57 | task :stats => 'cucumber:statsetup' 58 | rescue LoadError 59 | desc 'cucumber rake task not available (cucumber not installed)' 60 | task :cucumber do 61 | abort 'Cucumber rake task is not available. Be sure to install cucumber as a gem or plugin' 62 | end 63 | end 64 | 65 | end 66 | -------------------------------------------------------------------------------- /lib/tasks/sample_data.rake: -------------------------------------------------------------------------------- 1 | namespace :db do 2 | desc "Fill database with sample data" 3 | task populate: :environment do 4 | make_users 5 | make_microposts 6 | make_relationships 7 | end 8 | end 9 | 10 | def make_users 11 | admin = User.create!(name: "Example User", 12 | email: "example@railstutorial.org", 13 | password: "foobar", 14 | password_confirmation: "foobar") 15 | admin.toggle!(:admin) 16 | 99.times do |n| 17 | name = Faker::Name.name 18 | email = "example-#{n+1}@railstutorial.org" 19 | password = "password" 20 | User.create!(name: name, email: email, password: password, 21 | password_confirmation: password) 22 | end 23 | end 24 | 25 | def make_microposts 26 | users = User.all(limit: 6) 27 | 50.times do 28 | content = Faker::Lorem.sentence(5) 29 | users.each { |user| user.microposts.create!(content: content) } 30 | end 31 | end 32 | 33 | def make_relationships 34 | users = User.all 35 | user = users.first 36 | followed_users = users[2..50] 37 | followers = users[3..40] 38 | followed_users.each { |followed| user.follow!(followed) } 39 | followers.each { |follower| follower.follow!(user) } 40 | end -------------------------------------------------------------------------------- /log/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mhartl/sample_app_rails_3_2/4ec29458818683903ae1fed9a9647eb2d5e332f6/log/.gitkeep -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 17 | 18 | 19 | 20 | 21 |
    22 |

    The page you were looking for doesn't exist.

    23 |

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

    24 |
    25 | 26 | 27 | -------------------------------------------------------------------------------- /public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 17 | 18 | 19 | 20 | 21 |
    22 |

    The change you wanted was rejected.

    23 |

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

    24 |
    25 | 26 | 27 | -------------------------------------------------------------------------------- /public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 17 | 18 | 19 | 20 | 21 |
    22 |

    We're sorry, but something went wrong.

    23 |
    24 | 25 | 26 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mhartl/sample_app_rails_3_2/4ec29458818683903ae1fed9a9647eb2d5e332f6/public/favicon.ico -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file 2 | # 3 | # To ban all spiders from the entire site uncomment the next two lines: 4 | # User-Agent: * 5 | # Disallow: / 6 | -------------------------------------------------------------------------------- /script/cucumber: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | vendored_cucumber_bin = Dir["#{File.dirname(__FILE__)}/../vendor/{gems,plugins}/cucumber*/bin/cucumber"].first 4 | if vendored_cucumber_bin 5 | load File.expand_path(vendored_cucumber_bin) 6 | else 7 | require 'rubygems' unless ENV['NO_RUBYGEMS'] 8 | require 'cucumber' 9 | load Cucumber::BINARY 10 | end 11 | -------------------------------------------------------------------------------- /script/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application. 3 | 4 | APP_PATH = File.expand_path('../../config/application', __FILE__) 5 | require File.expand_path('../../config/boot', __FILE__) 6 | require 'rails/commands' 7 | -------------------------------------------------------------------------------- /spec/controllers/relationships_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe RelationshipsController do 4 | 5 | let(:user) { FactoryGirl.create(:user) } 6 | let(:other_user) { FactoryGirl.create(:user) } 7 | 8 | before { sign_in user } 9 | 10 | describe "creating a relationship with Ajax" do 11 | 12 | it "should increment the Relationship count" do 13 | expect do 14 | xhr :post, :create, relationship: { followed_id: other_user.id } 15 | end.should change(Relationship, :count).by(1) 16 | end 17 | 18 | it "should respond with success" do 19 | xhr :post, :create, relationship: { followed_id: other_user.id } 20 | response.should be_success 21 | end 22 | end 23 | 24 | describe "destroying a relationship with Ajax" do 25 | 26 | before { user.follow!(other_user) } 27 | let(:relationship) { user.relationships.find_by_followed_id(other_user) } 28 | 29 | it "should decrement the Relationship count" do 30 | expect do 31 | xhr :delete, :destroy, id: relationship.id 32 | end.should change(Relationship, :count).by(-1) 33 | end 34 | 35 | it "should respond with success" do 36 | xhr :delete, :destroy, id: relationship.id 37 | response.should be_success 38 | end 39 | end 40 | end -------------------------------------------------------------------------------- /spec/factories.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :user do 3 | sequence(:name) { |n| "Person #{n}" } 4 | sequence(:email) { |n| "person_#{n}@example.com" } 5 | password "foobar" 6 | password_confirmation "foobar" 7 | 8 | factory :admin do 9 | admin true 10 | end 11 | end 12 | 13 | factory :micropost do 14 | content "Lorem ipsum" 15 | user 16 | end 17 | end -------------------------------------------------------------------------------- /spec/helpers/application_helper_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe ApplicationHelper do 4 | 5 | describe "full_title" do 6 | 7 | it "should include the page title" do 8 | full_title('foo').should =~ /foo/ 9 | end 10 | 11 | it "should include the base title" do 12 | full_title('foo').should =~ /^Ruby on Rails Tutorial Sample App/ 13 | end 14 | 15 | it "should not include a bar on the home page" do 16 | full_title('').should_not =~ /\|/ 17 | end 18 | end 19 | end -------------------------------------------------------------------------------- /spec/models/micropost_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Micropost do 4 | 5 | let(:user) { FactoryGirl.create(:user) } 6 | 7 | before do 8 | @micropost = user.microposts.build(content: "Lorem ipsum") 9 | end 10 | 11 | subject { @micropost } 12 | 13 | it { should respond_to(:content) } 14 | it { should respond_to(:user_id) } 15 | it { should respond_to(:user) } 16 | its(:user) { should == user } 17 | 18 | it { should be_valid } 19 | 20 | describe "accessible attributes" do 21 | it "should not allow access to user_id" do 22 | expect do 23 | Micropost.new(user_id: "1") 24 | end.should raise_error(ActiveModel::MassAssignmentSecurity::Error) 25 | end 26 | end 27 | 28 | describe "when user_id is not present" do 29 | before { @micropost.user_id = nil } 30 | it { should_not be_valid } 31 | end 32 | 33 | describe "with blank content" do 34 | before { @micropost.content = " " } 35 | it { should_not be_valid } 36 | end 37 | 38 | describe "with content that is too long" do 39 | before { @micropost.content = "a" * 141 } 40 | it { should_not be_valid } 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /spec/models/relationship_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Relationship do 4 | 5 | let(:follower) { FactoryGirl.create(:user) } 6 | let(:followed) { FactoryGirl.create(:user) } 7 | let(:relationship) { follower.relationships.build(followed_id: followed.id) } 8 | 9 | subject { relationship } 10 | 11 | it { should be_valid } 12 | 13 | describe "accessible attributes" do 14 | it "should not allow access to follower_id" do 15 | expect do 16 | Relationship.new(follower_id: follower.id) 17 | end.should raise_error(ActiveModel::MassAssignmentSecurity::Error) 18 | end 19 | end 20 | 21 | describe "follower methods" do 22 | 23 | it { should respond_to(:follower) } 24 | it { should respond_to(:followed) } 25 | its(:follower) { should == follower } 26 | its(:followed) { should == followed } 27 | end 28 | 29 | describe "when follower id is not present" do 30 | before { relationship.follower_id = nil } 31 | it { should_not be_valid } 32 | end 33 | 34 | describe "when followed id is not present" do 35 | before { relationship.followed_id = nil } 36 | it { should_not be_valid } 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /spec/models/user_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe User do 4 | 5 | before do 6 | @user = User.new(name: "Example User", email: "user@example.com", 7 | password: "foobar", password_confirmation: "foobar") 8 | end 9 | 10 | subject { @user } 11 | 12 | it { should respond_to(:name) } 13 | it { should respond_to(:email) } 14 | it { should respond_to(:password_digest) } 15 | it { should respond_to(:password) } 16 | it { should respond_to(:password_confirmation) } 17 | it { should respond_to(:remember_token) } 18 | it { should respond_to(:admin) } 19 | it { should respond_to(:authenticate) } 20 | it { should respond_to(:microposts) } 21 | it { should respond_to(:feed) } 22 | it { should respond_to(:relationships) } 23 | it { should respond_to(:followed_users) } 24 | it { should respond_to(:reverse_relationships) } 25 | it { should respond_to(:followers) } 26 | it { should respond_to(:following?) } 27 | it { should respond_to(:follow!) } 28 | it { should respond_to(:unfollow!) } 29 | 30 | it { should be_valid } 31 | it { should_not be_admin } 32 | 33 | describe "accessible attributes" do 34 | it "should not allow access to admin" do 35 | expect do 36 | User.new(admin: "1") 37 | end.should raise_error(ActiveModel::MassAssignmentSecurity::Error) 38 | end 39 | end 40 | 41 | describe "with admin attribute set to 'true'" do 42 | before { @user.toggle!(:admin) } 43 | 44 | it { should be_admin } 45 | end 46 | 47 | describe "when name is not present" do 48 | before { @user.name = " " } 49 | it { should_not be_valid } 50 | end 51 | 52 | describe "when email is not present" do 53 | before { @user.email = " " } 54 | it { should_not be_valid } 55 | end 56 | 57 | describe "when name is too long" do 58 | before { @user.name = "a" * 51 } 59 | it { should_not be_valid } 60 | end 61 | 62 | describe "when email format is invalid" do 63 | it "should be invalid" do 64 | addresses = %w[user@foo,com user_at_foo.org example.user@foo. 65 | foo@bar_baz.com foo@bar+baz.com] 66 | addresses.each do |invalid_address| 67 | @user.email = invalid_address 68 | @user.should_not be_valid 69 | end 70 | end 71 | end 72 | 73 | describe "when email format is valid" do 74 | it "should be valid" do 75 | addresses = %w[user@foo.COM A_US-ER@f.b.org frst.lst@foo.jp a+b@baz.cn] 76 | addresses.each do |valid_address| 77 | @user.email = valid_address 78 | @user.should be_valid 79 | end 80 | end 81 | end 82 | 83 | describe "when email address is already taken" do 84 | before do 85 | user_with_same_email = @user.dup 86 | user_with_same_email.email = @user.email.upcase 87 | user_with_same_email.save 88 | end 89 | 90 | it { should_not be_valid } 91 | end 92 | 93 | describe "when password is not present" do 94 | before { @user.password = @user.password_confirmation = " " } 95 | it { should_not be_valid } 96 | end 97 | 98 | describe "when password doesn't match confirmation" do 99 | before { @user.password_confirmation = "mismatch" } 100 | it { should_not be_valid } 101 | end 102 | 103 | describe "when password confirmation is nil" do 104 | before { @user.password_confirmation = nil } 105 | it { should_not be_valid } 106 | end 107 | 108 | describe "when password is too short" do 109 | before { @user.password = @user.password_confirmation = "a" * 5 } 110 | it { should_not be_valid } 111 | end 112 | 113 | describe "return value of authenticate method" do 114 | before { @user.save } 115 | let(:found_user) { User.find_by_email(@user.email) } 116 | 117 | describe "with valid password" do 118 | it { should == found_user.authenticate(@user.password) } 119 | end 120 | 121 | describe "with invalid password" do 122 | let(:user_for_invalid_password) { found_user.authenticate("invalid") } 123 | 124 | it { should_not == user_for_invalid_password } 125 | specify { user_for_invalid_password.should be_false } 126 | end 127 | end 128 | 129 | describe "remember token" do 130 | before { @user.save } 131 | its(:remember_token) { should_not be_blank } 132 | end 133 | 134 | describe "micropost associations" do 135 | 136 | before { @user.save } 137 | let!(:older_micropost) do 138 | FactoryGirl.create(:micropost, user: @user, created_at: 1.day.ago) 139 | end 140 | let!(:newer_micropost) do 141 | FactoryGirl.create(:micropost, user: @user, created_at: 1.hour.ago) 142 | end 143 | 144 | it "should have the right microposts in the right order" do 145 | @user.microposts.should == [newer_micropost, older_micropost] 146 | end 147 | 148 | it "should destroy associated microposts" do 149 | microposts = @user.microposts 150 | @user.destroy 151 | microposts.each do |micropost| 152 | Micropost.find_by_id(micropost.id).should be_nil 153 | end 154 | end 155 | 156 | describe "status" do 157 | let(:unfollowed_post) do 158 | FactoryGirl.create(:micropost, user: FactoryGirl.create(:user)) 159 | end 160 | let(:followed_user) { FactoryGirl.create(:user) } 161 | 162 | before do 163 | @user.follow!(followed_user) 164 | 3.times { followed_user.microposts.create!(content: "Lorem ipsum") } 165 | end 166 | 167 | its(:feed) { should include(older_micropost) } 168 | its(:feed) { should include(newer_micropost) } 169 | its(:feed) { should_not include(unfollowed_post) } 170 | its(:feed) do 171 | followed_user.microposts.each do |micropost| 172 | should include(micropost) 173 | end 174 | end 175 | end 176 | end 177 | 178 | describe "following" do 179 | let(:other_user) { FactoryGirl.create(:user) } 180 | before do 181 | @user.save 182 | @user.follow!(other_user) 183 | end 184 | 185 | it { should be_following(other_user) } 186 | its(:followed_users) { should include(other_user) } 187 | 188 | describe "followed user" do 189 | subject { other_user } 190 | its(:followers) { should include(@user) } 191 | end 192 | 193 | describe "and unfollowing" do 194 | before { @user.unfollow!(other_user) } 195 | 196 | it { should_not be_following(other_user) } 197 | its(:followed_users) { should_not include(other_user) } 198 | end 199 | end 200 | end 201 | -------------------------------------------------------------------------------- /spec/requests/authentication_pages_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "Authentication" do 4 | 5 | subject { page } 6 | 7 | describe "signin page" do 8 | before { visit signin_path } 9 | 10 | it { should have_selector('h1', text: 'Sign in') } 11 | it { should have_selector('title', text: 'Sign in') } 12 | end 13 | 14 | describe "signin" do 15 | before { visit signin_path } 16 | 17 | describe "with invalid information" do 18 | before { click_button "Sign in" } 19 | 20 | it { should have_selector('title', text: 'Sign in') } 21 | it { should have_error_message } 22 | 23 | describe "after visiting another page" do 24 | before { click_link "Home" } 25 | it { should_not have_error_message } 26 | end 27 | end 28 | 29 | describe "with valid information" do 30 | let(:user) { FactoryGirl.create(:user) } 31 | before { sign_in user } 32 | 33 | it { should have_selector('title', text: user.name) } 34 | it { should have_link('Profile', href: user_path(user)) } 35 | it { should have_link('Sign out', href: signout_path) } 36 | it { should have_link('Settings', href: edit_user_path(user)) } 37 | it { should have_link('Users', href: users_path) } 38 | it { should_not have_link('Sign in', href: signin_path) } 39 | 40 | describe "followed by signout" do 41 | before { click_link "Sign out" } 42 | it { should have_link('Sign in') } 43 | end 44 | end 45 | end 46 | 47 | describe "authorization" do 48 | 49 | describe "for non-signed-in users" do 50 | let(:user) { FactoryGirl.create(:user) } 51 | 52 | describe "when attempting to visit a protected page" do 53 | before do 54 | visit edit_user_path(user) 55 | fill_in "Email", with: user.email 56 | fill_in "Password", with: user.password 57 | click_button "Sign in" 58 | end 59 | 60 | describe "after signing in" do 61 | it "should render the desired protected page" do 62 | page.should have_selector('title', text: 'Edit user') 63 | end 64 | 65 | describe "when signing in again" do 66 | before do 67 | click_link "Sign out" 68 | click_link "Sign in" 69 | fill_in "Email", with: user.email 70 | fill_in "Password", with: user.password 71 | click_button "Sign in" 72 | end 73 | 74 | it "should render the default (profile) page" do 75 | page.should have_selector('title', text: user.name) 76 | end 77 | end 78 | end 79 | end 80 | 81 | describe "in the Users controller" do 82 | 83 | describe "visiting the edit page" do 84 | before { visit edit_user_path(user) } 85 | it { should have_selector('title', text: 'Sign in') } 86 | it { should have_selector('div.alert.alert-notice') } 87 | end 88 | 89 | describe "submitting to the update action" do 90 | before { put user_path(user) } 91 | specify { response.should redirect_to(signin_path) } 92 | end 93 | 94 | describe "visiting the user index" do 95 | before { visit users_path } 96 | it { should have_selector('title', text: 'Sign in') } 97 | end 98 | 99 | describe "visiting the following page" do 100 | before { visit following_user_path(user) } 101 | it { should have_selector('title', text: 'Sign in') } 102 | end 103 | 104 | describe "visiting the followers page" do 105 | before { visit followers_user_path(user) } 106 | it { should have_selector('title', text: 'Sign in') } 107 | end 108 | end 109 | end 110 | 111 | describe "in the Microposts controller" do 112 | 113 | describe "submitting to the create action" do 114 | before { post microposts_path } 115 | specify { response.should redirect_to(signin_path)} 116 | end 117 | 118 | describe "submitting to the destroy action" do 119 | before { delete micropost_path(FactoryGirl.create(:micropost)) } 120 | specify { response.should redirect_to(signin_path) } 121 | end 122 | end 123 | 124 | describe "in the Relationships controller" do 125 | 126 | describe "submitting to the create action" do 127 | before { post relationships_path } 128 | specify { response.should redirect_to(signin_path)} 129 | end 130 | 131 | describe "submitting to the destroy action" do 132 | before { delete relationship_path(1) } 133 | specify { response.should redirect_to(signin_path) } 134 | end 135 | end 136 | 137 | describe "as wrong user" do 138 | let(:user) { FactoryGirl.create(:user) } 139 | let(:wrong_user) { FactoryGirl.create(:user, email: "wrong@example.com") } 140 | before { sign_in user } 141 | 142 | describe "visiting Users#edit page" do 143 | before { visit edit_user_path(wrong_user) } 144 | it { should_not have_selector('title', text: 'Edit user') } 145 | end 146 | 147 | describe "submitting a PUT request to the Users#update action" do 148 | before { put user_path(wrong_user) } 149 | specify { response.should redirect_to(root_path) } 150 | end 151 | end 152 | 153 | describe "as non-admin user" do 154 | let(:user) { FactoryGirl.create(:user) } 155 | let(:non_admin) { FactoryGirl.create(:user) } 156 | 157 | before { sign_in non_admin } 158 | 159 | describe "submitting a DELETE request to the Users#destroy action" do 160 | before { delete user_path(user) } 161 | specify { response.should redirect_to(root_path) } 162 | end 163 | end 164 | end 165 | end 166 | -------------------------------------------------------------------------------- /spec/requests/micropost_pages_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "Micropost pages" do 4 | 5 | subject { page } 6 | 7 | let(:user) { FactoryGirl.create(:user) } 8 | 9 | before { sign_in user } 10 | 11 | describe "micropost creation" do 12 | before { visit root_path } 13 | 14 | describe "with invalid information" do 15 | 16 | it "should not create a micropost" do 17 | expect { click_button "Post" }.not_to change(Micropost, :count) 18 | end 19 | 20 | describe "error messages" do 21 | before { click_button "Post" } 22 | it { should have_content('error') } 23 | end 24 | end 25 | end 26 | 27 | describe "micropost destruction" do 28 | before { FactoryGirl.create(:micropost, user: user) } 29 | 30 | describe "as correct user" do 31 | before { visit root_path } 32 | 33 | it "should delete a micropost" do 34 | expect { click_link "delete" }.should change(Micropost, :count).by(-1) 35 | end 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /spec/requests/static_pages_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "Static pages" do 4 | 5 | subject { page } 6 | 7 | describe "Home page" do 8 | before { visit root_path } 9 | 10 | it { should have_selector('h1', text: 'Sample App') } 11 | it { should have_selector('title', text: full_title('')) } 12 | it { should_not have_selector('title', text: '| Home') } 13 | 14 | describe "for signed-in users" do 15 | let(:user) { FactoryGirl.create(:user) } 16 | before do 17 | FactoryGirl.create(:micropost, user: user, content: "Lorem ipsum") 18 | FactoryGirl.create(:micropost, user: user, content: "Dolor sit amet") 19 | sign_in user 20 | visit root_path 21 | end 22 | 23 | it "should render the user's feed" do 24 | user.feed.each do |item| 25 | page.should have_selector("li##{item.id}", text: item.content) 26 | end 27 | end 28 | 29 | describe "follower/following counts" do 30 | let(:other_user) { FactoryGirl.create(:user) } 31 | before do 32 | other_user.follow!(user) 33 | visit root_path 34 | end 35 | 36 | it { should have_link("0 following", href: following_user_path(user)) } 37 | it { should have_link("1 followers", href: followers_user_path(user)) } 38 | end 39 | end 40 | end 41 | 42 | describe "Help page" do 43 | before { visit help_path } 44 | 45 | it { should have_selector('h1', text: 'Help') } 46 | it { should have_selector('title', text: full_title('Help')) } 47 | end 48 | 49 | describe "About page" do 50 | before { visit about_path } 51 | 52 | it { should have_selector('h1', text: 'About Us') } 53 | 54 | it { should have_selector('title', text: full_title('About Us')) } 55 | end 56 | 57 | describe "Contact page" do 58 | before { visit contact_path } 59 | 60 | it { should have_selector('h1', text: 'Contact') } 61 | it { should have_selector('title', text: full_title('Contact')) } 62 | end 63 | 64 | it "should have the right links on the layout" do 65 | visit root_path 66 | click_link "Sign in" 67 | page.should have_selector 'title', text: full_title('Sign in') 68 | click_link "About" 69 | page.should have_selector 'title', text: full_title('About Us') 70 | click_link "Help" 71 | page.should have_selector 'title', text: full_title('Help') 72 | click_link "Contact" 73 | page.should have_selector 'title', text: full_title('Contact') 74 | click_link "Home" 75 | click_link "Sign up now!" 76 | page.should have_selector 'title', text: full_title('Sign up') 77 | click_link "sample app" 78 | page.should have_selector 'h1', text: 'Sample App' 79 | end 80 | end 81 | -------------------------------------------------------------------------------- /spec/requests/user_pages_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe "User pages" do 4 | 5 | subject { page } 6 | 7 | describe "index" do 8 | 9 | let(:user) { FactoryGirl.create(:user) } 10 | 11 | before(:all) { 30.times { FactoryGirl.create(:user) } } 12 | after(:all) { User.delete_all } 13 | 14 | before(:each) do 15 | sign_in user 16 | visit users_path 17 | end 18 | 19 | it { should have_selector('title', text: 'All users') } 20 | it { should have_selector('h1', text: 'All users') } 21 | 22 | describe "pagination" do 23 | it { should have_selector('div.pagination') } 24 | 25 | it "should list each user" do 26 | User.paginate(page: 1).each do |user| 27 | page.should have_selector('li>a', text: user.name) 28 | end 29 | end 30 | end 31 | 32 | describe "delete links" do 33 | 34 | it { should_not have_link('delete') } 35 | 36 | describe "as an admin user" do 37 | let(:admin) { FactoryGirl.create(:admin) } 38 | before do 39 | sign_in admin 40 | visit users_path 41 | end 42 | 43 | it { should have_link('delete', href: user_path(User.first)) } 44 | it "should be able to delete another user" do 45 | expect { click_link('delete') }.to change(User, :count).by(-1) 46 | end 47 | it { should_not have_link('delete', href: user_path(admin)) } 48 | end 49 | end 50 | end 51 | 52 | describe "signup page" do 53 | before { visit signup_path } 54 | 55 | it { should have_selector('h1', text: 'Sign up') } 56 | it { should have_selector('title', text: full_title('Sign up')) } 57 | end 58 | 59 | describe "profile page" do 60 | let(:user) { FactoryGirl.create(:user) } 61 | let!(:m1) { FactoryGirl.create(:micropost, user: user, content: "Foo") } 62 | let!(:m2) { FactoryGirl.create(:micropost, user: user, content: "Bar") } 63 | 64 | before { visit user_path(user) } 65 | 66 | it { should have_selector('h1', text: user.name) } 67 | it { should have_selector('title', text: user.name) } 68 | 69 | describe "microposts" do 70 | it { should have_content(m1.content) } 71 | it { should have_content(m2.content) } 72 | it { should have_content(user.microposts.count) } 73 | end 74 | 75 | describe "follow/unfollow buttons" do 76 | let(:other_user) { FactoryGirl.create(:user) } 77 | before { sign_in user } 78 | 79 | describe "following a user" do 80 | before { visit user_path(other_user) } 81 | 82 | it "should increment the followed user count" do 83 | expect do 84 | click_button "Follow" 85 | end.to change(user.followed_users, :count).by(1) 86 | end 87 | 88 | it "should increment the other user's followers count" do 89 | expect do 90 | click_button "Follow" 91 | end.to change(other_user.followers, :count).by(1) 92 | end 93 | 94 | describe "toggling the button" do 95 | before { click_button "Follow" } 96 | it { should have_selector('input', value: 'Unfollow') } 97 | end 98 | end 99 | 100 | describe "unfollowing a user" do 101 | before do 102 | user.follow!(other_user) 103 | visit user_path(other_user) 104 | end 105 | 106 | it "should decrement the followed user count" do 107 | expect do 108 | click_button "Unfollow" 109 | end.to change(user.followed_users, :count).by(-1) 110 | end 111 | 112 | it "should decrement the other user's followers count" do 113 | expect do 114 | click_button "Unfollow" 115 | end.to change(other_user.followers, :count).by(-1) 116 | end 117 | 118 | describe "toggling the button" do 119 | before { click_button "Unfollow" } 120 | it { should have_selector('input', value: 'Follow') } 121 | end 122 | end 123 | end 124 | end 125 | 126 | describe "signup" do 127 | 128 | before { visit signup_path } 129 | 130 | let(:submit) { "Create my account" } 131 | 132 | describe "with invalid information" do 133 | it "should not create a user" do 134 | expect { click_button submit }.not_to change(User, :count) 135 | end 136 | 137 | describe "after submission" do 138 | before { click_button submit } 139 | 140 | it { should have_selector('title', text: 'Sign up') } 141 | it { should have_content('error') } 142 | it { should_not have_content('Password digest') } 143 | end 144 | end 145 | 146 | describe "with valid information" do 147 | 148 | before do 149 | fill_in "Name", with: "Example User" 150 | fill_in "Email", with: "user@example.com" 151 | fill_in "Password", with: "foobar" 152 | fill_in "Confirmation", with: "foobar" 153 | end 154 | 155 | it "should create a user" do 156 | expect { click_button submit }.to change(User, :count).by(1) 157 | end 158 | 159 | describe "after saving a user" do 160 | before { click_button submit } 161 | 162 | let(:user) { User.find_by_email("user@example.com") } 163 | 164 | it { should have_selector('title', text: user.name) } 165 | it { should have_selector('div.alert.alert-success', text: 'Welcome') } 166 | it { should have_link('Sign out') } 167 | end 168 | end 169 | end 170 | 171 | describe "edit" do 172 | let(:user) { FactoryGirl.create(:user) } 173 | before do 174 | sign_in user 175 | visit edit_user_path(user) 176 | end 177 | 178 | describe "page" do 179 | 180 | it { should have_selector('h1', text: "Update your profile") } 181 | it { should have_selector('title', text: "Edit user") } 182 | it { should have_link('change', href: 'http://gravatar.com/emails') } 183 | end 184 | 185 | describe "with invalid information" do 186 | before { click_button "Save changes" } 187 | 188 | it { should have_content('error') } 189 | end 190 | 191 | describe "with valid information" do 192 | let(:new_name) { "New Name" } 193 | let(:new_email) { "new@example.com" } 194 | before do 195 | fill_in "Name", with: new_name 196 | fill_in "Email", with: new_email 197 | fill_in "Password", with: user.password 198 | fill_in "Confirm Password", with: user.password 199 | click_button "Save changes" 200 | end 201 | 202 | it { should have_selector('title', text: new_name) } 203 | it { should have_link('Sign out', href: signout_path) } 204 | it { should have_selector('div.alert.alert-success') } 205 | specify { user.reload.name.should == new_name } 206 | specify { user.reload.email.should == new_email } 207 | end 208 | end 209 | 210 | describe "following/followers" do 211 | let(:user) { FactoryGirl.create(:user) } 212 | let(:other_user) { FactoryGirl.create(:user) } 213 | before { user.follow!(other_user) } 214 | 215 | describe "followed users (following)" do 216 | before do 217 | sign_in user 218 | visit following_user_path(user) 219 | end 220 | 221 | it { should have_selector('title', text: full_title('Following')) } 222 | it { should have_selector('h3', text: 'Following') } 223 | it { should have_link(other_user.name, href: user_path(other_user)) } 224 | end 225 | 226 | describe "followers" do 227 | before do 228 | sign_in other_user 229 | visit followers_user_path(other_user) 230 | end 231 | 232 | it { should have_selector('title', text: full_title('Followers')) } 233 | it { should have_selector('h3', text: 'Followers') } 234 | it { should have_link(user.name, href: user_path(user)) } 235 | end 236 | end 237 | end 238 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'spork' 3 | #uncomment the following line to use spork with the debugger 4 | #require 'spork/ext/ruby-debug' 5 | 6 | Spork.prefork do 7 | # Loading more in this block will cause your tests to run faster. However, 8 | # if you change any configuration or code from libraries loaded here, you'll 9 | # need to restart spork for it take effect. 10 | # This file is copied to spec/ when you run 'rails generate rspec:install' 11 | ENV["RAILS_ENV"] ||= 'test' 12 | require File.expand_path("../../config/environment", __FILE__) 13 | require 'rspec/rails' 14 | require 'rspec/autorun' 15 | 16 | # Requires supporting ruby files with custom matchers and macros, etc, 17 | # in spec/support/ and its subdirectories. 18 | Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f} 19 | RSpec.configure do |config| 20 | # ## Mock Framework 21 | # 22 | # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line: 23 | # 24 | # config.mock_with :mocha 25 | # config.mock_with :flexmock 26 | # config.mock_with :rr 27 | 28 | # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures 29 | config.fixture_path = "#{::Rails.root}/spec/fixtures" 30 | 31 | # If you're not using ActiveRecord, or you'd prefer not to run each of your 32 | # examples within a transaction, remove the following line or assign false 33 | # instead of true. 34 | config.use_transactional_fixtures = true 35 | 36 | # If true, the base class of anonymous controllers will be inferred 37 | # automatically. This will be the default behavior in future versions of 38 | # rspec-rails. 39 | config.infer_base_class_for_anonymous_controllers = false 40 | end 41 | end 42 | 43 | Spork.each_run do 44 | # This code will be run each time you run your specs. 45 | 46 | end 47 | 48 | # --- Instructions --- 49 | # Sort the contents of this file into a Spork.prefork and a Spork.each_run 50 | # block. 51 | # 52 | # The Spork.prefork block is run only once when the spork server is started. 53 | # You typically want to place most of your (slow) initializer code in here, in 54 | # particular, require'ing any 3rd-party gems that you don't normally modify 55 | # during development. 56 | # 57 | # The Spork.each_run block is run each time you run your specs. In case you 58 | # need to load files that tend to change during development, require them here. 59 | # With Rails, your application modules are loaded automatically, so sometimes 60 | # this block can remain empty. 61 | # 62 | # Note: You can modify files loaded *from* the Spork.each_run block without 63 | # restarting the spork server. However, this file itself will not be reloaded, 64 | # so if you change any of the code inside the each_run block, you still need to 65 | # restart the server. In general, if you have non-trivial code in this file, 66 | # it's advisable to move it into a separate file so you can easily edit it 67 | # without restarting spork. (For example, with RSpec, you could move 68 | # non-trivial code into a file spec/support/my_helper.rb, making sure that the 69 | # spec/support/* files are require'd from inside the each_run block.) 70 | # 71 | # Any code that is left outside the two blocks will be run during preforking 72 | # *and* during each_run -- that's probably not what you want. 73 | # 74 | # These instructions should self-destruct in 10 seconds. If they don't, feel 75 | # free to delete them. 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /spec/support/utilities.rb: -------------------------------------------------------------------------------- 1 | include ApplicationHelper 2 | 3 | RSpec::Matchers.define :have_error_message do |message| 4 | match do |page| 5 | page.should have_selector('div.alert.alert-error', text: 'Invalid') 6 | end 7 | end 8 | 9 | def sign_in(user) 10 | visit signin_path 11 | fill_in "Email", with: user.email 12 | fill_in "Password", with: user.password 13 | click_button "Sign in" 14 | # Sign in when not using Capybara. 15 | cookies[:remember_token] = user.remember_token 16 | end -------------------------------------------------------------------------------- /vendor/assets/javascripts/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mhartl/sample_app_rails_3_2/4ec29458818683903ae1fed9a9647eb2d5e332f6/vendor/assets/javascripts/.gitkeep -------------------------------------------------------------------------------- /vendor/assets/stylesheets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mhartl/sample_app_rails_3_2/4ec29458818683903ae1fed9a9647eb2d5e332f6/vendor/assets/stylesheets/.gitkeep -------------------------------------------------------------------------------- /vendor/plugins/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mhartl/sample_app_rails_3_2/4ec29458818683903ae1fed9a9647eb2d5e332f6/vendor/plugins/.gitkeep --------------------------------------------------------------------------------