├── log └── .keep ├── app ├── mailers │ ├── .keep │ ├── application_mailer.rb │ └── user_mailer.rb ├── models │ ├── .keep │ ├── concerns │ │ └── .keep │ ├── relationship.rb │ ├── micropost.rb │ └── user.rb ├── assets │ ├── images │ │ ├── .keep │ │ └── rails.png │ ├── stylesheets │ │ ├── users.scss │ │ ├── sessions.scss │ │ ├── microposts.scss │ │ ├── relationships.scss │ │ ├── static_pages.scss │ │ ├── password_resets.scss │ │ ├── acount_activations.scss │ │ ├── application.css │ │ └── custom.css.scss │ └── javascripts │ │ ├── users.coffee │ │ ├── microposts.coffee │ │ ├── sessions.coffee │ │ ├── password_resets.coffee │ │ ├── relationships.coffee │ │ ├── static_pages.coffee │ │ ├── acount_activations.coffee │ │ └── application.js ├── controllers │ ├── concerns │ │ └── .keep │ ├── static_pages_controller.rb │ ├── application_controller.rb │ ├── account_activations_controller.rb │ ├── relationships_controller.rb │ ├── sessions_controller.rb │ ├── microposts_controller.rb │ ├── password_resets_controller.rb │ └── users_controller.rb ├── views │ ├── layouts │ │ ├── mailer.text.erb │ │ ├── mailer.html.erb │ │ ├── _shim.html.erb │ │ ├── _footer.html.erb │ │ ├── application.html.erb │ │ └── _header.html.erb │ ├── static_pages │ │ ├── home.html.erb │ │ ├── contact.html.erb │ │ ├── help.html.erb │ │ ├── about.html.erb │ │ ├── _anonymous_user.erb │ │ └── _signed_in.erb │ ├── relationships │ │ ├── create.js.erb │ │ └── destroy.js.erb │ ├── shared │ │ ├── _feed.html.erb │ │ ├── _user_info.html.erb │ │ ├── _error_messages.html.erb │ │ ├── _stats.html.erb │ │ └── _micropost_form.html.erb │ ├── users │ │ ├── index.html.erb │ │ ├── _follow.html.erb │ │ ├── _unfollow.html.erb │ │ ├── new.html.erb │ │ ├── _follow_form.html.erb │ │ ├── _user.html.erb │ │ ├── edit.html.erb │ │ ├── _form.html.erb │ │ ├── show.html.erb │ │ └── show_follow.html.erb │ ├── user_mailer │ │ ├── account_activation.text.erb │ │ ├── password_reset.text.erb │ │ ├── account_activation.html.erb │ │ └── password_reset.html.erb │ ├── password_resets │ │ ├── new.html.erb │ │ └── edit.html.erb │ ├── microposts │ │ └── _micropost.html.erb │ └── sessions │ │ └── new.html.erb ├── helpers │ ├── microposts_helper.rb │ ├── static_pages_helper.rb │ ├── password_resets_helper.rb │ ├── relationships_helper.rb │ ├── acount_activations_helper.rb │ ├── application_helper.rb │ ├── users_helper.rb │ └── sessions_helper.rb └── uploaders │ └── picture_uploader.rb ├── lib ├── assets │ └── .keep └── tasks │ └── .keep ├── public ├── favicon.ico ├── robots.txt ├── 500.html ├── 422.html └── 404.html ├── test ├── helpers │ ├── .keep │ ├── application_helper_test.rb │ └── sessions_helper_test.rb ├── mailers │ ├── .keep │ ├── previews │ │ └── user_mailer_preview.rb │ └── user_mailer_test.rb ├── models │ ├── .keep │ ├── relationship_test.rb │ ├── micropost_test.rb │ └── user_test.rb ├── controllers │ ├── .keep │ ├── sessions_controller_test.rb │ ├── relationships_controller_test.rb │ ├── static_pages_controller_test.rb │ ├── microposts_controller_test.rb │ └── users_controller_test.rb ├── fixtures │ ├── .keep │ ├── rails.png │ ├── relationships.yml │ ├── users.yml │ └── microposts.yml ├── integration │ ├── .keep │ ├── site_layout_test.rb │ ├── users_profile_test.rb │ ├── users_index_test.rb │ ├── users_login_test.rb │ ├── microposts_interface_test.rb │ ├── users_signup_test.rb │ ├── users_edit_test.rb │ ├── following_test.rb │ └── password_resets_test.rb └── test_helper.rb ├── vendor └── assets │ ├── javascripts │ └── .keep │ └── stylesheets │ └── .keep ├── .Procfile ├── spring └── 50c4ccb1d8f630979e84c03ba14676d1.pid ├── bin ├── bundle ├── rake ├── rails ├── spring └── setup ├── config ├── boot.rb ├── initializers │ ├── skip_image_resizing.rb │ ├── cookies_serializer.rb │ ├── session_store.rb │ ├── mime_types.rb │ ├── filter_parameter_logging.rb │ ├── carrier_wave.rb │ ├── backtrace_silencers.rb │ ├── assets.rb │ ├── wrap_parameters.rb │ └── inflections.rb ├── environment.rb ├── puma.rb ├── database.yml ├── locales │ └── en.yml ├── routes.rb ├── secrets.yml ├── application.rb └── environments │ ├── development.rb │ ├── test.rb │ └── production.rb ├── config.ru ├── db ├── migrate │ ├── 20160225061655_add_admin_to_users.rb │ ├── 20160224011359_add_index_to_users_email.rb │ ├── 20160229053312_add_picture_to_microposts.rb │ ├── 20160224012956_add_password_digest_to_users.rb │ ├── 20160225004732_add_remember_digest_to_users.rb │ ├── 20160226104214_add_reset_to_users.rb │ ├── 20160223235829_create_users.rb │ ├── 20160225093821_add_activation_to_users.rb │ ├── 20160227022814_create_microposts.rb │ └── 20160301020419_create_relationships.rb ├── seeds.rb └── schema.rb ├── README.md ├── Rakefile ├── .gitignore ├── Gemfile ├── Guardfile └── Gemfile.lock /log/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/mailers/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/models/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/assets/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/tasks/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/helpers/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/mailers/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/models/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/assets/images/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/controllers/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/integration/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/models/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.Procfile: -------------------------------------------------------------------------------- 1 | web: bundle exec puma -C config/puma.rb 2 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.text.erb: -------------------------------------------------------------------------------- 1 | <%= yield %> 2 | -------------------------------------------------------------------------------- /spring/50c4ccb1d8f630979e84c03ba14676d1.pid: -------------------------------------------------------------------------------- 1 | 3425 2 | -------------------------------------------------------------------------------- /app/helpers/microposts_helper.rb: -------------------------------------------------------------------------------- 1 | module MicropostsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/static_pages_helper.rb: -------------------------------------------------------------------------------- 1 | module StaticPagesHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/password_resets_helper.rb: -------------------------------------------------------------------------------- 1 | module PasswordResetsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/relationships_helper.rb: -------------------------------------------------------------------------------- 1 | module RelationshipsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/acount_activations_helper.rb: -------------------------------------------------------------------------------- 1 | module AcountActivationsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | <%= yield %> 4 | 5 | 6 | -------------------------------------------------------------------------------- /test/fixtures/rails.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaptainStack/sample-app/master/test/fixtures/rails.png -------------------------------------------------------------------------------- /app/assets/images/rails.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaptainStack/sample-app/master/app/assets/images/rails.png -------------------------------------------------------------------------------- /bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: "noreply@example.com" 3 | layout 'mailer' 4 | end 5 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 2 | 3 | require 'bundler/setup' # Set up gems listed in the Gemfile. 4 | -------------------------------------------------------------------------------- /app/views/layouts/_shim.html.erb: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /config/initializers/skip_image_resizing.rb: -------------------------------------------------------------------------------- 1 | if Rails.env.test? 2 | CarrierWave.configure do |config| 3 | config.enable_processing = false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/views/static_pages/home.html.erb: -------------------------------------------------------------------------------- 1 | <% if logged_in? %> 2 | <%= render 'static_pages/signed_in' %> 3 | <% else %> 4 | <%= render 'anonymous_user' %> 5 | <% end %> 6 | -------------------------------------------------------------------------------- /app/views/relationships/create.js.erb: -------------------------------------------------------------------------------- 1 | $("#follow_form").html("<%= escape_javascript(render('users/unfollow')) %>"); 2 | $("#followers").html('<%= @user.followers.count %>'); 3 | -------------------------------------------------------------------------------- /app/views/relationships/destroy.js.erb: -------------------------------------------------------------------------------- 1 | $("#follow_form").html("<%= escape_javascript(render('users/follow')) %>"); 2 | $("#followers").html('<%= @user.followers.count %>'); 3 | -------------------------------------------------------------------------------- /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 Rails.application 5 | -------------------------------------------------------------------------------- /db/migrate/20160225061655_add_admin_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddAdminToUsers < ActiveRecord::Migration 2 | def change 3 | add_column :users, :admin, :boolean 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.action_dispatch.cookies_serializer = :json 4 | -------------------------------------------------------------------------------- /app/views/shared/_feed.html.erb: -------------------------------------------------------------------------------- 1 | <% if @feed_items.any? %> 2 |
    3 | <%= render @feed_items %> 4 |
5 | <%= will_paginate @feed_items %> 6 | <% end %> 7 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require File.expand_path('../application', __FILE__) 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.session_store :cookie_store, key: '_sample_app_session' 4 | -------------------------------------------------------------------------------- /db/migrate/20160224011359_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/20160229053312_add_picture_to_microposts.rb: -------------------------------------------------------------------------------- 1 | class AddPictureToMicroposts < ActiveRecord::Migration 2 | def change 3 | add_column :microposts, :picture, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /db/migrate/20160224012956_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/20160225004732_add_remember_digest_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddRememberDigestToUsers < ActiveRecord::Migration 2 | def change 3 | add_column :users, :remember_digest, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/assets/stylesheets/users.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the Users controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/sessions.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the Sessions controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/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/assets/stylesheets/microposts.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the Microposts controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/relationships.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the Relationships controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/static_pages.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the StaticPages controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /db/migrate/20160226104214_add_reset_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddResetToUsers < ActiveRecord::Migration 2 | def change 3 | add_column :users, :reset_digest, :string 4 | add_column :users, :reset_sent_at, :datetime 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /app/assets/stylesheets/password_resets.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the PasswordResets controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/robotstxt.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 | -------------------------------------------------------------------------------- /app/assets/stylesheets/acount_activations.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the AcountActivations controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/views/user_mailer/account_activation.text.erb: -------------------------------------------------------------------------------- 1 | Hi <%= @user.name %>, 2 | 3 | Welcome to the Sample App! Click on the link below to activate your account: 4 | 5 | <%= edit_account_activation_url(@user.activation_token, email: @user.email) %> 6 | -------------------------------------------------------------------------------- /app/views/users/_follow.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_for(current_user.active_relationships.build, remote: true) do |f| %> 2 |
<%= hidden_field_tag :followed_id, @user.id %>
3 | <%= f.submit "Follow", class: "btn btn-primary" %> 4 | <% end %> 5 | -------------------------------------------------------------------------------- /test/controllers/sessions_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class SessionsControllerTest < ActionController::TestCase 4 | test "should get new" do 5 | get :new 6 | assert_response :success 7 | end 8 | 9 | end 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ruby on Rails Tutorial: sample application 2 | 3 | This is the sample application for the 4 | [*Ruby on Rails Tutorial: 5 | Learn Web Development with Rails*](http://www.railstutorial.org/) 6 | by [Michael Hartl](http://www.michaelhartl.com/). -------------------------------------------------------------------------------- /app/assets/javascripts/users.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://coffeescript.org/ 4 | -------------------------------------------------------------------------------- /app/views/static_pages/contact.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, "Contact") %> 2 |

Contact

3 |

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

7 | -------------------------------------------------------------------------------- /config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /app/assets/javascripts/microposts.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://coffeescript.org/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/sessions.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://coffeescript.org/ 4 | -------------------------------------------------------------------------------- /app/models/relationship.rb: -------------------------------------------------------------------------------- 1 | class Relationship < ActiveRecord::Base 2 | belongs_to :follower, class_name: "User" 3 | belongs_to :followed, class_name: "User" 4 | validates :follower_id, presence: true 5 | validates :followed_id, presence: true 6 | end 7 | -------------------------------------------------------------------------------- /app/views/users/_unfollow.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_for(current_user.active_relationships.find_by(followed_id: @user.id), 2 | html: { method: :delete }, 3 | remote: true) do |f| %> 4 | <%= f.submit "Unfollow", class: "btn" %> 5 | <% end %> 6 | -------------------------------------------------------------------------------- /app/views/users/new.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, 'Sign up') %> 2 | <% provide(:button_text, 'Create my account') %> 3 |

Sign up

4 |
5 |
6 | <%= render 'form' %> 7 |
8 |
9 | -------------------------------------------------------------------------------- /app/assets/javascripts/password_resets.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://coffeescript.org/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/relationships.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://coffeescript.org/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/static_pages.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://coffeescript.org/ 4 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path('../spring', __FILE__) 4 | rescue LoadError => e 5 | raise unless e.message.include?('spring') 6 | end 7 | require_relative '../config/boot' 8 | require 'rake' 9 | Rake.application.run 10 | -------------------------------------------------------------------------------- /app/assets/javascripts/acount_activations.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://coffeescript.org/ 4 | -------------------------------------------------------------------------------- /db/migrate/20160223235829_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 null: false 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require File.expand_path('../config/application', __FILE__) 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /app/views/shared/_user_info.html.erb: -------------------------------------------------------------------------------- 1 | <%= link_to gravatar_for(current_user, size: 50), current_user %> 2 |

<%= current_user.name %>

3 | <%= link_to "view my profile", current_user %> 4 | <%= pluralize(current_user.microposts.count, "micropost") %> 5 | -------------------------------------------------------------------------------- /app/views/users/_follow_form.html.erb: -------------------------------------------------------------------------------- 1 | <% unless current_user?(@user) %> 2 |
3 | <% if current_user.following?(@user) %> 4 | <%= render 'unfollow' %> 5 | <% else %> 6 | <%= render 'follow' %> 7 | <% end %> 8 |
9 | <% end %> 10 | -------------------------------------------------------------------------------- /test/fixtures/relationships.yml: -------------------------------------------------------------------------------- 1 | one: 2 | follower: michael 3 | followed: lana 4 | 5 | two: 6 | follower: michael 7 | followed: malory 8 | 9 | three: 10 | follower: lana 11 | followed: michael 12 | 13 | four: 14 | follower: archer 15 | followed: michael 16 | -------------------------------------------------------------------------------- /app/views/users/_user.html.erb: -------------------------------------------------------------------------------- 1 |
  • 2 | <%= gravatar_for user, size: 50 %> 3 | <%= link_to user.name, user %> 4 | <% if current_user.admin? && !current_user?(user) %> 5 | | <%= link_to "delete", user, method: :delete, data: { confirm: "You sure?" } %> 6 | <% end %> 7 |
  • 8 | -------------------------------------------------------------------------------- /db/migrate/20160225093821_add_activation_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddActivationToUsers < ActiveRecord::Migration 2 | def change 3 | add_column :users, :activation_digest, :string 4 | add_column :users, :activated, :boolean, default: false 5 | add_column :users, :activated_at, :datetime 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path('../spring', __FILE__) 4 | rescue LoadError => e 5 | raise unless e.message.include?('spring') 6 | end 7 | APP_PATH = File.expand_path('../../config/application', __FILE__) 8 | require_relative '../config/boot' 9 | require 'rails/commands' 10 | -------------------------------------------------------------------------------- /test/helpers/application_helper_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class ApplicationHelperTest < ActionView::TestCase 4 | test "full title helper" do 5 | assert_equal full_title, "Ruby on Rails Tutorial Sample App" 6 | assert_equal full_title("Help"), "Help | Ruby on Rails Tutorial Sample App" 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /app/mailers/user_mailer.rb: -------------------------------------------------------------------------------- 1 | class UserMailer < ApplicationMailer 2 | 3 | def account_activation(user) 4 | @user = user 5 | mail to: user.email, subject: "Account activation" 6 | end 7 | 8 | def password_reset(user) 9 | @user = user 10 | mail to: user.email, subject: "Password reset" 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/views/user_mailer/password_reset.text.erb: -------------------------------------------------------------------------------- 1 | To reset your password click the link below: 2 | 3 | <%= edit_password_reset_url(@user.reset_token, email: @user.email) %> 4 | 5 | This link will expire in two hours. 6 | 7 | If you did not request your password to be reset, please ignore this email and 8 | your password will stay as it is. 9 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | 3 | # Returns the full title on a per-page basis. 4 | def full_title(page_title = '') 5 | base_title = "Ruby on Rails Tutorial Sample App" 6 | if page_title.empty? 7 | base_title 8 | else 9 | page_title + " | " + base_title 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/helpers/users_helper.rb: -------------------------------------------------------------------------------- 1 | module UsersHelper 2 | def gravatar_for(user, options = { size: 80 }) 3 | gravatar_id = Digest::MD5::hexdigest(user.email.downcase) 4 | size = options[:size] 5 | gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}?s=#{size}" 6 | image_tag(gravatar_url, alt: user.name, class: "gravatar") 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20160227022814_create_microposts.rb: -------------------------------------------------------------------------------- 1 | class CreateMicroposts < ActiveRecord::Migration 2 | def change 3 | create_table :microposts do |t| 4 | t.text :content 5 | t.references :user, index: true, foreign_key: true 6 | 7 | t.timestamps null: false 8 | end 9 | add_index :microposts, [:user_id, :created_at] 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/views/user_mailer/account_activation.html.erb: -------------------------------------------------------------------------------- 1 |

    Sample App

    2 | 3 |

    Hi <%= @user.name %>,

    4 | 5 |

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

    8 | 9 | <%= link_to "Activate", edit_account_activation_url(@user.activation_token, 10 | email: @user.email) %> 11 | -------------------------------------------------------------------------------- /app/views/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 section. 6 | To get help on this sample app, see the 7 | Ruby on Rails Tutorial 8 | book. 9 |

    10 | -------------------------------------------------------------------------------- /app/controllers/static_pages_controller.rb: -------------------------------------------------------------------------------- 1 | class StaticPagesController < ApplicationController 2 | def home 3 | if logged_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/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 %> 13 | -------------------------------------------------------------------------------- /config/initializers/carrier_wave.rb: -------------------------------------------------------------------------------- 1 | if Rails.env.production? 2 | CarrierWave.configure do |config| 3 | config.fog_credentials = { 4 | # Configuration for Amazon S3 5 | :provider => 'AWS', 6 | :aws_access_key_id => ENV['S3_ACCESS_KEY'], 7 | :aws_secret_access_key => ENV['S3_SECRET_KEY'] 8 | } 9 | config.fog_directory = ENV['S3_BUCKET'] 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/views/users/edit.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, 'Edit user') %> 2 | <% provide(:button_text, 'Save changes') %> 3 |

    Update your profile

    4 |
    5 |
    6 | <%= render 'form' %> 7 |
    8 | <%= gravatar_for @user %> 9 | Change 10 |
    11 |
    12 |
    13 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | protect_from_forgery with: :exception 3 | include SessionsHelper 4 | 5 | private 6 | 7 | # Confirms a logged-in user. 8 | def logged_in_user 9 | unless logged_in? 10 | store_location 11 | flash[:danger] = "Please log in." 12 | redirect_to login_url 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/views/password_resets/new.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, "Forgot password") %> 2 |

    Forgot password

    3 | 4 |
    5 |
    6 | <%= form_for(:password_reset, url: password_resets_path) do |f| %> 7 | <%= f.label :email %> 8 | <%= f.email_field :email, class: 'form-control' %> 9 | 10 | <%= f.submit "Submit", class: "btn btn-primary" %> 11 | <% end %> 12 |
    13 |
    14 | -------------------------------------------------------------------------------- /app/views/shared/_stats.html.erb: -------------------------------------------------------------------------------- 1 | <% @user ||= current_user %> 2 |
    3 | 4 | 5 | <%= @user.following.count %> 6 | 7 | following 8 | 9 | 10 | 11 | <%= @user.followers.count %> 12 | 13 | followers 14 | 15 |
    16 | -------------------------------------------------------------------------------- /db/migrate/20160301020419_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 null: false 8 | end 9 | add_index :relationships, :follower_id 10 | add_index :relationships, :followed_id 11 | add_index :relationships, [:follower_id, :followed_id], unique: true 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/views/layouts/_footer.html.erb: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /app/views/static_pages/about.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, "About") %> 2 |

    About

    3 |

    4 | The Ruby on Rails 5 | Tutorial is a 6 | book and 7 | screencast series 8 | to teach web development with 9 | Ruby on Rails. 10 | This is the sample application for the tutorial. 11 |

    12 | -------------------------------------------------------------------------------- /app/views/user_mailer/password_reset.html.erb: -------------------------------------------------------------------------------- 1 |

    Password reset

    2 | 3 |

    To reset your password click the link below:

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

    This link will expire in two hours.

    9 | 10 |

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

    14 | -------------------------------------------------------------------------------- /app/views/static_pages/_anonymous_user.erb: -------------------------------------------------------------------------------- 1 |
    2 |

    Welcome to the Sample App

    3 | 4 |

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

    9 | 10 | <%= link_to "Sign up now!", signup_path, class: "btn btn-lg btn-primary" %> 11 |
    12 | 13 | <%= link_to image_tag("rails.png", alt: "Rails logo"), 14 | 'http://rubyonrails.org/' %> 15 | -------------------------------------------------------------------------------- /app/views/static_pages/_signed_in.erb: -------------------------------------------------------------------------------- 1 |
    2 | 13 |
    14 |

    Micropost Feed

    15 | <%= render 'shared/feed' %> 16 |
    17 |
    18 | -------------------------------------------------------------------------------- /app/controllers/account_activations_controller.rb: -------------------------------------------------------------------------------- 1 | class AccountActivationsController < ApplicationController 2 | 3 | def edit 4 | user = User.find_by(email: params[:email]) 5 | if user && !user.activated? && user.authenticated?(:activation, params[:id]) 6 | user.activate 7 | log_in user 8 | flash[:success] = "Account activated!" 9 | redirect_to user 10 | else 11 | flash[:danger] = "Invalid activation link" 12 | redirect_to root_url 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /test/integration/site_layout_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class SiteLayoutTest < ActionDispatch::IntegrationTest 4 | 5 | test "layout links" do 6 | get root_path 7 | assert_template 'static_pages/home' 8 | assert_select "a[href=?]", root_path, count: 2 9 | assert_select "a[href=?]", help_path 10 | assert_select "a[href=?]", about_path 11 | assert_select "a[href=?]", contact_path 12 | get signup_path 13 | assert_select "title", full_title("Sign up") 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /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 | Rails.backtrace_cleaner.add_silencer { |line| line =~ /rvm/ } 6 | 7 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 8 | # Rails.backtrace_cleaner.remove_silencers! 9 | -------------------------------------------------------------------------------- /bin/spring: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # This file loads spring without using Bundler, in order to be fast. 4 | # It gets overwritten when you run the `spring binstub` command. 5 | 6 | unless defined?(Spring) 7 | require 'rubygems' 8 | require 'bundler' 9 | 10 | if (match = Bundler.default_lockfile.read.match(/^GEM$.*?^ (?: )*spring \((.*?)\)$.*?^$/m)) 11 | Gem.paths = { 'GEM_PATH' => [Bundler.bundle_path.to_s, *Gem.path].uniq } 12 | gem 'spring', match[1] 13 | require 'spring/binstub' 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/models/micropost.rb: -------------------------------------------------------------------------------- 1 | class Micropost < ActiveRecord::Base 2 | belongs_to :user 3 | default_scope -> { order(created_at: :desc) } 4 | mount_uploader :picture, PictureUploader 5 | validates :user_id, presence: true 6 | validates :content, presence: true, length: { maximum: 140 } 7 | validate :picture_size 8 | 9 | private 10 | 11 | # Validates the size of an uploaded picture. 12 | def picture_size 13 | if picture.size > 5.megabytes 14 | errors.add(:picture, "should be less than 5MB") 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Version of your assets, change this if you want to expire all your assets. 4 | Rails.application.config.assets.version = '1.0' 5 | 6 | # Add additional assets to the asset load path 7 | # Rails.application.config.assets.paths << Emoji.images_path 8 | 9 | # Precompile additional assets. 10 | # application.js, application.css, and all non-JS/CSS in app/assets folder are already added. 11 | # Rails.application.config.assets.precompile += %w( search.js ) 12 | -------------------------------------------------------------------------------- /config/puma.rb: -------------------------------------------------------------------------------- 1 | workers Integer(ENV['WEB_CONCURRENCY'] || 2) 2 | threads_count = Integer(ENV['MAX_THREADS'] || 5) 3 | threads threads_count, threads_count 4 | 5 | preload_app! 6 | 7 | rackup DefaultRackup 8 | port ENV['PORT'] || 3000 9 | environment ENV['RACK_ENV'] || 'development' 10 | 11 | on_worker_boot do 12 | # Worker specific setup for Rails 4.1+ 13 | # See: https://devcenter.heroku.com/articles/ 14 | # deploying-rails-applications-with-the-puma-web-server#on-worker-boot 15 | ActiveRecord::Base.establish_connection 16 | end 17 | -------------------------------------------------------------------------------- /test/helpers/sessions_helper_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class SessionsHelperTest < ActionView::TestCase 4 | 5 | def setup 6 | @user = users(:michael) 7 | remember(@user) 8 | end 9 | 10 | test "current_user returns right user when session is nil" do 11 | assert_equal @user, current_user 12 | assert is_logged_in? 13 | end 14 | 15 | test "current_user returns nil when remember digest is wrong" do 16 | @user.update_attribute(:remember_digest, User.digest(User.new_token)) 17 | assert_nil current_user 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /test/controllers/relationships_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class RelationshipsControllerTest < ActionController::TestCase 4 | 5 | test "create should require logged-in user" do 6 | assert_no_difference 'Relationship.count' do 7 | post :create 8 | end 9 | assert_redirected_to login_url 10 | end 11 | 12 | test "destroy should require logged-in user" do 13 | assert_no_difference 'Relationship.count' do 14 | delete :destroy, id: relationships(:one) 15 | end 16 | assert_redirected_to login_url 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /test/models/relationship_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class RelationshipTest < ActiveSupport::TestCase 4 | 5 | def setup 6 | @relationship = Relationship.new(follower_id: 1, followed_id: 2) 7 | end 8 | 9 | test "should be valid" do 10 | assert @relationship.valid? 11 | end 12 | 13 | test "should require a follower_id" do 14 | @relationship.follower_id = nil 15 | assert_not @relationship.valid? 16 | end 17 | 18 | test "should require a followed_id" do 19 | @relationship.followed_id = nil 20 | assert_not @relationship.valid? 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /app/controllers/relationships_controller.rb: -------------------------------------------------------------------------------- 1 | class RelationshipsController < ApplicationController 2 | before_action :logged_in_user 3 | 4 | def create 5 | @user = User.find(params[:followed_id]) 6 | current_user.follow(@user) 7 | respond_to do |format| 8 | format.html { redirect_to @user } 9 | format.js 10 | end 11 | end 12 | 13 | def destroy 14 | @user = Relationship.find(params[:id]).followed 15 | current_user.unfollow(@user) 16 | respond_to do |format| 17 | format.html { redirect_to @user } 18 | format.js 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /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] if respond_to?(:wrap_parameters) 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /app/views/users/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_for(@user) do |f| %> 2 | <%= render 'shared/error_messages', object: f.object %> 3 | 4 | <%= f.label :name %> 5 | <%= f.text_field :name, class: 'form-control' %> 6 | 7 | <%= f.label :email %> 8 | <%= f.email_field :email, class: 'form-control' %> 9 | 10 | <%= f.label :password %> 11 | <%= f.password_field :password, class: 'form-control' %> 12 | 13 | <%= f.label :password_confirmation %> 14 | <%= f.password_field :password_confirmation, class: 'form-control' %> 15 | 16 | <%= f.submit yield(:button_text), class: "btn btn-primary" %> 17 | <% end %> 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile '~/.gitignore_global' 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore the default SQLite database. 11 | /db/*.sqlite3 12 | /db/*.sqlite3-journal 13 | 14 | # Ignore all logfiles and tempfiles. 15 | /log/* 16 | !/log/.keep 17 | /tmp 18 | 19 | # Ignore Spring files. 20 | /spring/*.pid 21 | 22 | # Ignore uploaded test images. 23 | /public/uploads 24 | -------------------------------------------------------------------------------- /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 | # 7 | default: &default 8 | adapter: sqlite3 9 | pool: 5 10 | timeout: 5000 11 | 12 | development: 13 | <<: *default 14 | database: db/development.sqlite3 15 | 16 | # Warning: The database defined as "test" will be erased and 17 | # re-generated from your development database when you run "rake". 18 | # Do not set this db to the same as development or production. 19 | test: 20 | <<: *default 21 | database: db/test.sqlite3 22 | 23 | production: 24 | <<: *default 25 | database: db/production.sqlite3 26 | -------------------------------------------------------------------------------- /test/mailers/previews/user_mailer_preview.rb: -------------------------------------------------------------------------------- 1 | # Preview all emails at http://localhost:3000/rails/mailers/user_mailer 2 | class UserMailerPreview < ActionMailer::Preview 3 | 4 | # Preview this email at http://localhost:3000/rails/mailers/user_mailer/account_activation 5 | def account_activation 6 | user = User.first 7 | user.activation_token = User.new_token 8 | UserMailer.account_activation(user) 9 | end 10 | 11 | # Preview this email at http://localhost:3000/rails/mailers/user_mailer/password_reset 12 | def password_reset 13 | user = User.first 14 | user.reset_token = User.new_token 15 | UserMailer.password_reset(user) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /test/mailers/user_mailer_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UserMailerTest < ActionMailer::TestCase 4 | 5 | test "account_activation" do 6 | user = users(:michael) 7 | user.reset_token = User.new_token 8 | user.activation_token = User.new_token 9 | mail = UserMailer.account_activation(user) 10 | assert_equal "Account activation", mail.subject 11 | assert_equal [user.email], mail.to 12 | assert_equal ["noreply@example.com"], mail.from 13 | assert_match user.name, mail.body.encoded 14 | assert_match user.activation_token, mail.body.encoded 15 | assert_match CGI::escape(user.email), mail.body.encoded 16 | end 17 | end -------------------------------------------------------------------------------- /app/uploaders/picture_uploader.rb: -------------------------------------------------------------------------------- 1 | class PictureUploader < CarrierWave::Uploader::Base 2 | include CarrierWave::MiniMagick 3 | process resize_to_limit: [400, 400] 4 | 5 | if Rails.env.production? 6 | storage :fog 7 | else 8 | storage :file 9 | end 10 | 11 | # Override the directory where uploaded files will be stored. 12 | # This is a sensible default for uploaders that are meant to be mounted: 13 | def store_dir 14 | "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" 15 | end 16 | 17 | # Add a white list of extensions which are allowed to be uploaded. 18 | def extension_white_list 19 | %w(jpg jpeg gif png) 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /app/views/microposts/_micropost.html.erb: -------------------------------------------------------------------------------- 1 |
  • 2 | <%= link_to gravatar_for(micropost.user, size: 50), micropost.user %> 3 | <%= link_to micropost.user.name, micropost.user %> 4 | 5 | <%= micropost.content %> 6 | <%= image_tag micropost.picture.url if micropost.picture? %> 7 | 8 | 9 | Posted <%= time_ago_in_words(micropost.created_at) %> ago. 10 | <% if current_user?(micropost.user) %> 11 | <%= link_to "delete", micropost, method: :delete, 12 | data: { confirm: "You sure?" } %> 13 | <% end %> 14 | 15 |
  • 16 | -------------------------------------------------------------------------------- /test/integration/users_profile_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UsersProfileTest < ActionDispatch::IntegrationTest 4 | include ApplicationHelper 5 | 6 | def setup 7 | @user = users(:michael) 8 | end 9 | 10 | test "profile display" do 11 | get user_path(@user) 12 | assert_template 'users/show' 13 | assert_select 'title', full_title(@user.name) 14 | assert_select 'h1', text: @user.name 15 | assert_select 'h1>img.gravatar' 16 | assert_match @user.microposts.count.to_s, response.body 17 | assert_select 'div.pagination' 18 | @user.microposts.paginate(page: 1).each do |micropost| 19 | assert_match micropost.content, response.body 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /app/views/users/show.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, @user.name) %> 2 |
    3 | 14 |
    15 | <%= render 'follow_form' if logged_in? %> 16 | <% if @user.microposts.any? %> 17 |

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

    18 |
      19 | <%= render @microposts %> 20 |
    21 | <%= will_paginate @microposts %> 22 | <% end %> 23 |
    24 |
    -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, '\1en' 8 | # inflect.singular /^(ox)en/i, '\1' 9 | # inflect.irregular 'person', 'people' 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym 'RESTful' 16 | # end 17 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # To learn more, please read the Rails Internationalization guide 20 | # available at http://guides.rubyonrails.org/i18n.html. 21 | 22 | en: 23 | hello: "Hello world" 24 | -------------------------------------------------------------------------------- /app/views/password_resets/edit.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, 'Reset password') %> 2 |

    Reset password

    3 | 4 |
    5 |
    6 | <%= form_for(@user, url: password_reset_path(params[:id])) do |f| %> 7 | <%= render 'shared/error_messages', object: f.object %> 8 | 9 | <%= hidden_field_tag :email, @user.email %> 10 | 11 | <%= f.label :password %> 12 | <%= f.password_field :password, class: 'form-control' %> 13 | 14 | <%= f.label :password_confirmation, "Confirmation" %> 15 | <%= f.password_field :password_confirmation, class: 'form-control' %> 16 | 17 | <%= f.submit "Update password", class: "btn btn-primary" %> 18 | <% end %> 19 |
    20 |
    21 | -------------------------------------------------------------------------------- /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 any plugin's vendor/assets/javascripts directory 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 | // compiled file. 9 | // 10 | // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details 11 | // about supported directives. 12 | // 13 | //= require jquery 14 | //= require jquery_ujs 15 | //= require bootstrap 16 | //= require turbolinks 17 | //= require_tree . 18 | -------------------------------------------------------------------------------- /app/views/shared/_micropost_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_for(@micropost, html: { multipart: true }) do |f| %> 2 | <%= render 'shared/error_messages', object: f.object %> 3 |
    4 | <%= f.text_area :content, placeholder: "Compose new micropost..." %> 5 |
    6 | <%= f.submit "Post", class: "btn btn-primary" %> 7 | 8 | <%= f.file_field :picture, accept: 'image/jpeg,image/gif,image/png' %> 9 | 10 | <% end %> 11 | 12 | 20 | -------------------------------------------------------------------------------- /app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which 3 | * will include all the files listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, 6 | * vendor/assets/stylesheets, or vendor/assets/stylesheets of plugins, if any, 7 | * can be referenced here using a relative path. 8 | * 9 | * You're free to add application-wide styles to this file and they'll appear 10 | * at the bottom of the compiled file so the styles you add here take 11 | * precedence over styles defined in any styles defined in the other CSS/SCSS 12 | * files in this directory. It is generally better to create a new file per 13 | * style scope. 14 | * 15 | *= require_tree . 16 | *= require_self 17 | */ 18 | -------------------------------------------------------------------------------- /test/controllers/static_pages_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class StaticPagesControllerTest < ActionController::TestCase 4 | test "should get home" do 5 | get :home 6 | assert_response :success 7 | assert_select "title", "Ruby on Rails Tutorial Sample App" 8 | end 9 | 10 | test "should get help" do 11 | get :help 12 | assert_response :success 13 | assert_select "title", "Help | Ruby on Rails Tutorial Sample App" 14 | end 15 | 16 | test "should get about" do 17 | get :about 18 | assert_response :success 19 | assert_select "title", "About | Ruby on Rails Tutorial Sample App" 20 | end 21 | 22 | test "should get contact" do 23 | get :contact 24 | assert_response :success 25 | assert_select "title", "Contact | Ruby on Rails Tutorial Sample App" 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= full_title(yield(:title)) %> 5 | <%= stylesheet_link_tag 'application', media: 'all', 6 | 'data-turbolinks-track' => true %> 7 | <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %> 8 | <%= csrf_meta_tags %> 9 | <%= render 'layouts/shim' %> 10 | 11 | 12 | <%= render 'layouts/header' %> 13 |
    14 | <% flash.each do |message_type, message| %> 15 | <%= content_tag(:div, message, class: "alert alert-#{message_type}") %> 16 | <% end %> 17 | <%= yield %> 18 | <%= render 'layouts/footer' %> 19 | <%= debug(params) if Rails.env.development? %> 20 |
    21 | 22 | 23 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | get 'password_resets/new' 3 | 4 | get 'password_resets/edit' 5 | 6 | root 'static_pages#home' 7 | get 'help' => 'static_pages#help' 8 | get 'about' => 'static_pages#about' 9 | get 'contact' => 'static_pages#contact' 10 | get 'signup' => 'users#new' 11 | get 'login' => 'sessions#new' 12 | post 'login' => 'sessions#create' 13 | delete 'logout' => 'sessions#destroy' 14 | resources :users do 15 | member do 16 | get :following, :followers 17 | end 18 | end 19 | resources :users 20 | resources :account_activations, only: [:edit] 21 | resources :password_resets, only: [:new, :create, :edit, :update] 22 | resources :microposts, only: [:create, :destroy] 23 | resources :relationships, only: [:create, :destroy] 24 | end 25 | -------------------------------------------------------------------------------- /app/views/sessions/new.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, "Log in") %> 2 |

    Log in

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

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

    24 |
    25 |
    -------------------------------------------------------------------------------- /app/controllers/sessions_controller.rb: -------------------------------------------------------------------------------- 1 | class SessionsController < ApplicationController 2 | 3 | def new 4 | end 5 | 6 | def create 7 | @user = User.find_by(email: params[:session][:email].downcase) 8 | if @user && @user.authenticate(params[:session][:password]) 9 | if @user.activated? 10 | log_in @user 11 | params[:session][:remember_me] == '1' ? remember(@user) : forget(@user) 12 | redirect_back_or @user 13 | else 14 | message = "Account not activated. " 15 | message += "Check your email for the activation link." 16 | flash[:warning] = message 17 | redirect_to root_url 18 | end 19 | else 20 | flash.now[:danger] = 'Invalid email/password combination' 21 | render 'new' 22 | end 23 | end 24 | 25 | def destroy 26 | log_out if logged_in? 27 | redirect_to root_url 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | 4 | # path to your application root. 5 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 6 | 7 | Dir.chdir APP_ROOT do 8 | # This script is a starting point to setup your application. 9 | # Add necessary setup steps to this file: 10 | 11 | puts "== Installing dependencies ==" 12 | system "gem install bundler --conservative" 13 | system "bundle check || bundle install" 14 | 15 | # puts "\n== Copying sample files ==" 16 | # unless File.exist?("config/database.yml") 17 | # system "cp config/database.yml.sample config/database.yml" 18 | # end 19 | 20 | puts "\n== Preparing database ==" 21 | system "bin/rake db:setup" 22 | 23 | puts "\n== Removing old logs and tempfiles ==" 24 | system "rm -f log/*" 25 | system "rm -rf tmp/cache" 26 | 27 | puts "\n== Restarting application server ==" 28 | system "touch tmp/restart.txt" 29 | end 30 | -------------------------------------------------------------------------------- /test/models/micropost_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class MicropostTest < ActiveSupport::TestCase 4 | 5 | def setup 6 | @user = users(:michael) 7 | # This code is not idiomatically correct. 8 | @micropost = @user.microposts.build(content: "Lorem ipsum") 9 | end 10 | 11 | test "should be valid" do 12 | assert @micropost.valid? 13 | end 14 | 15 | test "user id should be present" do 16 | @micropost.user_id = nil 17 | assert_not @micropost.valid? 18 | end 19 | 20 | test "content should be present" do 21 | @micropost.content = " " 22 | assert_not @micropost.valid? 23 | end 24 | 25 | test "content should be at most 140 characters" do 26 | @micropost.content = "a" * 141 27 | assert_not @micropost.valid? 28 | end 29 | 30 | test "order should be most recent first" do 31 | assert_equal microposts(:most_recent), Micropost.first 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /app/controllers/microposts_controller.rb: -------------------------------------------------------------------------------- 1 | class MicropostsController < ApplicationController 2 | before_action :logged_in_user, only: [:create, :destroy] 3 | before_action :correct_user, only: :destroy 4 | 5 | def create 6 | @micropost = current_user.microposts.build(micropost_params) 7 | if @micropost.save 8 | flash[:success] = "Micropost created!" 9 | redirect_to root_url 10 | else 11 | @feed_items = [] 12 | render 'static_pages/home' 13 | end 14 | end 15 | 16 | def destroy 17 | @micropost.destroy 18 | flash[:success] = "Micropost deleted" 19 | redirect_to request.referrer || root_url 20 | end 21 | 22 | private 23 | 24 | def micropost_params 25 | params.require(:micropost).permit(:content, :picture) 26 | end 27 | 28 | def correct_user 29 | @micropost = current_user.microposts.find_by(id: params[:id]) 30 | redirect_to root_url if @micropost.nil? 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /test/controllers/microposts_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class MicropostsControllerTest < ActionController::TestCase 4 | 5 | def setup 6 | @micropost = microposts(:orange) 7 | end 8 | 9 | test "should redirect create when not logged in" do 10 | assert_no_difference 'Micropost.count' do 11 | post :create, micropost: { content: "Lorem ipsum" } 12 | end 13 | assert_redirected_to login_url 14 | end 15 | 16 | test "should redirect destroy when not logged in" do 17 | assert_no_difference 'Micropost.count' do 18 | delete :destroy, id: @micropost 19 | end 20 | assert_redirected_to login_url 21 | end 22 | 23 | test "should redirect destroy for wrong micropost" do 24 | log_in_as(users(:michael)) 25 | micropost = microposts(:ants) 26 | assert_no_difference 'Micropost.count' do 27 | delete :destroy, id: micropost 28 | end 29 | assert_redirected_to root_url 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /app/views/users/show_follow.html.erb: -------------------------------------------------------------------------------- 1 | <% provide(:title, @title) %> 2 |
    3 | 21 |
    22 |

    <%= @title %>

    23 | <% if @users.any? %> 24 | 27 | <%= will_paginate %> 28 | <% end %> 29 |
    30 |
    31 | -------------------------------------------------------------------------------- /test/integration/users_index_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UsersIndexTest < ActionDispatch::IntegrationTest 4 | 5 | def setup 6 | @admin = users(:michael) 7 | @non_admin = users(:archer) 8 | end 9 | 10 | test "index as admin including pagination and delete links" do 11 | log_in_as(@admin) 12 | get users_path 13 | assert_template 'users/index' 14 | assert_select 'div.pagination' 15 | first_page_of_users = User.paginate(page: 1) 16 | first_page_of_users.each do |user| 17 | assert_select 'a[href=?]', user_path(user), text: user.name 18 | unless user == @admin 19 | assert_select 'a[href=?]', user_path(user), text: 'delete' 20 | end 21 | end 22 | assert_difference 'User.count', -1 do 23 | delete user_path(@non_admin) 24 | end 25 | end 26 | 27 | test "index as non-admin" do 28 | log_in_as(@non_admin) 29 | get users_path 30 | assert_select 'a', text: 'delete', count: 0 31 | end 32 | end -------------------------------------------------------------------------------- /config/secrets.yml: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key is used for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | 6 | # Make sure the secret is at least 30 characters and all random, 7 | # no regular words or you'll be exposed to dictionary attacks. 8 | # You can use `rake secret` to generate a secure secret key. 9 | 10 | # Make sure the secrets in this file are kept private 11 | # if you're sharing your code publicly. 12 | 13 | development: 14 | secret_key_base: bb823ae575348c91fbc5e73e71ee42dc2efa73605a77500fdcd23e381a42c8ed3ee402947c112e9142461c844f8ad3571b4018cd20513117439bbb3df159c938 15 | 16 | test: 17 | secret_key_base: 69a8996cfd4c36002cebdc2d7690df9cadc11a2853e1349dfe73373c15fc36d48265c992436205016cc9f95e118ba9cce4693ee38af0639efcfe3570e268c61d 18 | 19 | # Do not keep production secrets in the repository, 20 | # instead read values from the environment. 21 | production: 22 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> 23 | -------------------------------------------------------------------------------- /test/fixtures/users.yml: -------------------------------------------------------------------------------- 1 | michael: 2 | name: Michael Example 3 | email: michael@example.com 4 | password_digest: <%= User.digest('password') %> 5 | admin: true 6 | activated: true 7 | activated_at: <%= Time.zone.now %> 8 | 9 | archer: 10 | name: Sterling Archer 11 | email: duchess@example.gov 12 | password_digest: <%= User.digest('password') %> 13 | activated: true 14 | activated_at: <%= Time.zone.now %> 15 | 16 | lana: 17 | name: Lana Kane 18 | email: hands@example.gov 19 | password_digest: <%= User.digest('password') %> 20 | activated: true 21 | activated_at: <%= Time.zone.now %> 22 | 23 | malory: 24 | name: Malory Archer 25 | email: boss@example.gov 26 | password_digest: <%= User.digest('password') %> 27 | activated: true 28 | activated_at: <%= Time.zone.now %> 29 | 30 | <% 30.times do |n| %> 31 | user_<%= n %>: 32 | name: <%= "User #{n}" %> 33 | email: <%= "user-#{n}@example.com" %> 34 | password_digest: <%= User.digest('password') %> 35 | activated: true 36 | activated_at: <%= Time.zone.now %> 37 | <% end %> 38 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | ENV['RAILS_ENV'] ||= 'test' 2 | require File.expand_path('../../config/environment', __FILE__) 3 | require 'rails/test_help' 4 | require "minitest/reporters" 5 | Minitest::Reporters.use! 6 | 7 | class ActiveSupport::TestCase 8 | fixtures :all 9 | include ApplicationHelper 10 | 11 | # Returns true if a test user is logged in. 12 | def is_logged_in? 13 | !session[:user_id].nil? 14 | end 15 | 16 | # Logs in a test user. 17 | def log_in_as(user, options = {}) 18 | password = options[:password] || 'password' 19 | remember_me = options[:remember_me] || '1' 20 | if integration_test? 21 | post login_path, session: { email: user.email, 22 | password: password, 23 | remember_me: remember_me } 24 | else 25 | session[:user_id] = user.id 26 | end 27 | end 28 | 29 | private 30 | 31 | # Returns true inside an integration test. 32 | def integration_test? 33 | defined?(post_via_redirect) 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- 1 | # Users 2 | User.create!(name: "Example User", 3 | email: "example@railstutorial.org", 4 | password: "foobar", 5 | password_confirmation: "foobar", 6 | admin: true, 7 | activated: true, 8 | activated_at: Time.zone.now) 9 | 10 | 99.times do |n| 11 | name = Faker::Name.name 12 | email = "example-#{n+1}@railstutorial.org" 13 | password = "password" 14 | User.create!(name: name, 15 | email: email, 16 | password: password, 17 | password_confirmation: password, 18 | activated: true, 19 | activated_at: Time.zone.now) 20 | end 21 | 22 | # Microposts 23 | users = User.order(:created_at).take(6) 24 | 50.times do 25 | content = Faker::Lorem.sentence(5) 26 | users.each { |user| user.microposts.create!(content: content) } 27 | end 28 | 29 | # Following relationships 30 | users = User.all 31 | user = users.first 32 | following = users[2..50] 33 | followers = users[3..40] 34 | following.each { |followed| user.follow(followed) } 35 | followers.each { |follower| follower.follow(user) } 36 | -------------------------------------------------------------------------------- /app/views/layouts/_header.html.erb: -------------------------------------------------------------------------------- 1 | 30 | -------------------------------------------------------------------------------- /test/fixtures/microposts.yml: -------------------------------------------------------------------------------- 1 | orange: 2 | content: "I just ate an orange!" 3 | created_at: <%= 10.minutes.ago %> 4 | user: michael 5 | 6 | tau_manifesto: 7 | content: "Check out the @tauday site by @mhartl: http://tauday.com" 8 | created_at: <%= 3.years.ago %> 9 | 10 | cat_video: 11 | content: "Sad cats are sad: http://youtu.be/PKffm2uI4dk" 12 | created_at: <%= 2.hours.ago %> 13 | 14 | most_recent: 15 | content: "Writing a short test" 16 | created_at: <%= Time.zone.now %> 17 | 18 | <% 30.times do |n| %> 19 | micropost_<%= n %>: 20 | content: <%= Faker::Lorem.sentence(5) %> 21 | created_at: <%= 42.days.ago %> 22 | user: michael 23 | <% end %> 24 | 25 | ants: 26 | content: "Oh, is that what you want? Because that's how you get ants!" 27 | created_at: <%= 2.years.ago %> 28 | user: archer 29 | 30 | zone: 31 | content: "Danger zone!" 32 | created_at: <%= 3.days.ago %> 33 | user: archer 34 | 35 | tone: 36 | content: "I'm sorry. Your words made sense, but your sarcastic tone did not." 37 | created_at: <%= 10.minutes.ago %> 38 | user: lana 39 | 40 | van: 41 | content: "Dude, this van's, like, rolling probable cause." 42 | created_at: <%= 4.hours.ago %> 43 | user: lana -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'rails', '4.2.2' 4 | gem 'bcrypt', '3.1.7' 5 | gem 'faker', '1.4.2' 6 | gem 'carrierwave', '0.10.0' 7 | gem 'mini_magick', '3.8.0' 8 | gem 'fog', '1.36.0' 9 | gem 'will_paginate', '3.0.7' 10 | gem 'bootstrap-will_paginate', '0.0.10' 11 | gem 'bootstrap-sass', '3.2.0.0' 12 | gem 'sass-rails', '5.0.2' 13 | gem 'uglifier', '2.5.3' 14 | gem 'coffee-rails', '4.1.0' 15 | gem 'jquery-rails', '4.0.3' 16 | gem 'turbolinks', '2.3.0' 17 | gem 'jbuilder', '2.2.3' 18 | gem 'sdoc', '0.4.0', group: :doc 19 | 20 | group :development, :test do 21 | gem 'sqlite3', '1.3.9' 22 | gem 'byebug', '3.4.0' 23 | gem 'web-console', '2.0.0.beta3' 24 | gem 'spring', '1.1.3' 25 | end 26 | 27 | group :test do 28 | gem 'minitest-reporters', '1.0.5' 29 | gem 'mini_backtrace', '0.1.3' 30 | gem 'guard-minitest', '2.3.1' 31 | end 32 | 33 | group :production do 34 | gem 'pg', '0.17.1' 35 | gem 'rails_12factor', '0.0.2' 36 | gem 'puma', '2.11.1' 37 | end 38 | -------------------------------------------------------------------------------- /test/integration/users_login_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UsersLoginTest < ActionDispatch::IntegrationTest 4 | 5 | def setup 6 | @user = users(:michael) 7 | end 8 | 9 | test "login with valid information followed by logout" do 10 | get login_path 11 | post login_path, session: { email: @user.email, password: 'password' } 12 | assert is_logged_in? 13 | follow_redirect! 14 | assert_template 'users/show' 15 | assert_select "a[href=?]", login_path, count: 0 16 | assert_select "a[href=?]", logout_path 17 | assert_select "a[href=?]", user_path(@user) 18 | delete logout_path 19 | assert_not is_logged_in? 20 | assert_redirected_to root_url 21 | # Simulate a user clicking logout in a second window. 22 | delete logout_path 23 | follow_redirect! 24 | assert_select "a[href=?]", login_path 25 | assert_select "a[href=?]", logout_path, count: 0 26 | assert_select "a[href=?]", user_path(@user), count: 0 27 | end 28 | 29 | test "login with remembering" do 30 | log_in_as(@user, remember_me: '1') 31 | assert_equal cookies['remember_token'], assigns(:user).remember_token 32 | end 33 | 34 | test "login without remembering" do 35 | log_in_as(@user, remember_me: '0') 36 | assert_nil cookies['remember_token'] 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../boot', __FILE__) 2 | 3 | require 'rails/all' 4 | 5 | # Require the gems listed in Gemfile, including any gems 6 | # you've limited to :test, :development, or :production. 7 | Bundler.require(*Rails.groups) 8 | 9 | module SampleApp 10 | class Application < Rails::Application 11 | # Settings in config/environments/* take precedence over those specified here. 12 | # Application configuration should go into files in config/initializers 13 | # -- all .rb files in that directory are automatically loaded. 14 | 15 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 16 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 17 | # config.time_zone = 'Central Time (US & Canada)' 18 | 19 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. 20 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] 21 | # config.i18n.default_locale = :de 22 | 23 | # Do not swallow errors in after_commit/after_rollback callbacks. 24 | config.active_record.raise_in_transactional_callbacks = true 25 | 26 | # Include the authenticity token in remote forms. 27 | config.action_view.embed_authenticity_token_in_remote_forms = true 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /app/helpers/sessions_helper.rb: -------------------------------------------------------------------------------- 1 | module SessionsHelper 2 | 3 | # Logs in the given user. 4 | def log_in(user) 5 | session[:user_id] = user.id 6 | end 7 | 8 | # Forgets a persistent session. 9 | def forget(user) 10 | user.forget 11 | cookies.delete(:user_id) 12 | cookies.delete(:remember_token) 13 | end 14 | 15 | # Logs out the current user. 16 | def log_out 17 | forget(current_user) 18 | session.delete(:user_id) 19 | @current_user = nil 20 | end 21 | 22 | # Remembers a user in a persistent session. 23 | def remember(user) 24 | user.remember 25 | cookies.permanent.signed[:user_id] = user.id 26 | cookies.permanent[:remember_token] = user.remember_token 27 | end 28 | 29 | # Returns the current logged-in user (if any). 30 | def current_user 31 | if (user_id = session[:user_id]) 32 | @current_user ||= User.find_by(id: user_id) 33 | elsif (user_id = cookies.signed[:user_id]) 34 | user = User.find_by(id: user_id) 35 | if user && user.authenticated?(:remember, cookies[:remember_token]) 36 | log_in user 37 | @current_user = user 38 | end 39 | end 40 | end 41 | 42 | # Returns true if the user is logged in, false otherwise. 43 | def logged_in? 44 | !current_user.nil? 45 | end 46 | 47 | def current_user?(user) 48 | user == current_user 49 | end 50 | 51 | # Redirects to stored location (or to the default). 52 | def redirect_back_or(default) 53 | redirect_to(session[:forwarding_url] || default) 54 | session.delete(:forwarding_url) 55 | end 56 | 57 | # Stores the URL trying to be accessed. 58 | def store_location 59 | session[:forwarding_url] = request.url if request.get? 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
    60 |
    61 |

    We're sorry, but something went wrong.

    62 |
    63 |

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

    64 |
    65 | 66 | 67 | -------------------------------------------------------------------------------- /test/integration/microposts_interface_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class MicropostsInterfaceTest < ActionDispatch::IntegrationTest 4 | 5 | def setup 6 | @user = users(:michael) 7 | end 8 | 9 | test "micropost interface" do 10 | log_in_as(@user) 11 | get root_path 12 | assert_select 'div.pagination' 13 | assert_select 'input[type=file]' 14 | # Invalid submission 15 | post microposts_path, micropost: { content: "" } 16 | assert_select 'div#error_explanation' 17 | # Valid submission 18 | content = "This micropost really ties the room together" 19 | picture = fixture_file_upload('test/fixtures/rails.png', 'image/png') 20 | assert_difference 'Micropost.count', 1 do 21 | post microposts_path, micropost: { content: content, picture: picture } 22 | end 23 | assert assigns(:micropost).picture? 24 | follow_redirect! 25 | assert_match content, response.body 26 | # Delete a post. 27 | assert_select 'a', 'delete' 28 | first_micropost = @user.microposts.paginate(page: 1).first 29 | assert_difference 'Micropost.count', -1 do 30 | delete micropost_path(first_micropost) 31 | end 32 | # Visit a different user. 33 | get user_path(users(:archer)) 34 | assert_select 'a', { text: 'delete', count: 0 } 35 | end 36 | 37 | test "micropost sidebar count" do 38 | log_in_as(@user) 39 | get root_path 40 | assert_match "#{@user.microposts.count} microposts", response.body 41 | # User with zero microposts 42 | other_user = users(:malory) 43 | log_in_as(other_user) 44 | get root_path 45 | assert_match "0 microposts", response.body 46 | other_user.microposts.create!(content: "A micropost") 47 | get root_path 48 | assert_match "1 micropost", response.body 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
    60 |
    61 |

    The change you wanted was rejected.

    62 |

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

    63 |
    64 |

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

    65 |
    66 | 67 | 68 | -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
    60 |
    61 |

    The page you were looking for doesn't exist.

    62 |

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

    63 |
    64 |

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

    65 |
    66 | 67 | 68 | -------------------------------------------------------------------------------- /test/integration/users_signup_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UsersSignupTest < ActionDispatch::IntegrationTest 4 | 5 | def setup 6 | ActionMailer::Base.deliveries.clear 7 | end 8 | 9 | test "invalid signup information" do 10 | get signup_path 11 | assert_no_difference 'User.count' do 12 | post users_path, user: { name: "", 13 | email: "user@invalid", 14 | password: "foo", 15 | password_confirmation: "bar" } 16 | end 17 | assert_template 'users/new' 18 | assert_select 'div#error_explanation' 19 | assert_select 'div.field_with_errors' 20 | end 21 | 22 | test "valid signup information with account activation" do 23 | get signup_path 24 | assert_difference 'User.count', 1 do 25 | post users_path, user: { name: "Example User", 26 | email: "user@example.com", 27 | password: "password", 28 | password_confirmation: "password" } 29 | end 30 | assert_equal 1, ActionMailer::Base.deliveries.size 31 | user = assigns(:user) 32 | assert_not user.activated? 33 | # Try to log in before activation. 34 | log_in_as(user) 35 | assert_not is_logged_in? 36 | # Invalid activation token 37 | get edit_account_activation_path("invalid token") 38 | assert_not is_logged_in? 39 | # Valid token, wrong email 40 | get edit_account_activation_path(user.activation_token, email: 'wrong') 41 | assert_not is_logged_in? 42 | # Valid activation token 43 | get edit_account_activation_path(user.activation_token, email: user.email) 44 | assert user.reload.activated? 45 | follow_redirect! 46 | assert_template 'users/show' 47 | assert is_logged_in? 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /app/controllers/password_resets_controller.rb: -------------------------------------------------------------------------------- 1 | class PasswordResetsController < ApplicationController 2 | before_action :get_user, only: [:edit, :update] 3 | before_action :valid_user, only: [:edit, :update] 4 | before_action :check_expiration, only: [:edit, :update] 5 | 6 | def new 7 | end 8 | 9 | def create 10 | @user = User.find_by(email: params[:password_reset][:email].downcase) 11 | if @user 12 | @user.create_reset_digest 13 | @user.send_password_reset_email 14 | flash[:info] = "Email sent with password reset instructions" 15 | redirect_to root_url 16 | else 17 | flash.now[:danger] = "Email address not found" 18 | render 'new' 19 | end 20 | end 21 | 22 | def edit 23 | end 24 | 25 | def update 26 | if params[:user][:password].empty? 27 | @user.errors.add(:password, "can't be empty") 28 | render 'edit' 29 | elsif @user.update_attributes(user_params) 30 | log_in @user 31 | flash[:success] = "Password has been reset." 32 | redirect_to @user 33 | else 34 | render 'edit' 35 | end 36 | end 37 | 38 | private 39 | 40 | def user_params 41 | params.require(:user).permit(:password, :password_confirmation) 42 | end 43 | 44 | # Before filters 45 | 46 | def get_user 47 | @user = User.find_by(email: params[:email]) 48 | end 49 | 50 | # Confirms a valid user. 51 | def valid_user 52 | unless (@user && @user.activated? && 53 | @user.authenticated?(:reset, params[:id])) 54 | redirect_to root_url 55 | end 56 | end 57 | 58 | # Checks expiration of reset token. 59 | def check_expiration 60 | if @user.password_reset_expired? 61 | flash[:danger] = "Password reset has expired." 62 | redirect_to new_password_reset_url 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /test/integration/users_edit_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UsersEditTest < ActionDispatch::IntegrationTest 4 | 5 | def setup 6 | @user = users(:michael) 7 | end 8 | 9 | test "unsuccessful edit" do 10 | log_in_as(@user) 11 | get edit_user_path(@user) 12 | assert_template 'users/edit' 13 | patch user_path(@user), user: { name: "", 14 | email: "foo@invalid", 15 | password: "foo", 16 | password_confirmation: "bar" } 17 | assert_template 'users/edit' 18 | end 19 | 20 | test "successful edit" do 21 | log_in_as(@user) 22 | get edit_user_path(@user) 23 | assert_template 'users/edit' 24 | name = "Foo Bar" 25 | email = "foo@bar.com" 26 | patch user_path(@user), user: { name: name, 27 | email: email, 28 | password: "", 29 | password_confirmation: "" } 30 | assert_not flash.empty? 31 | assert_redirected_to @user 32 | @user.reload 33 | assert_equal name, @user.name 34 | assert_equal email, @user.email 35 | end 36 | 37 | test "successful edit with friendly forwarding" do 38 | get edit_user_path(@user) 39 | log_in_as(@user) 40 | assert_redirected_to edit_user_path(@user) 41 | name = "Foo Bar" 42 | email = "foo@bar.com" 43 | patch user_path(@user), user: { name: name, 44 | email: email, 45 | password: "", 46 | password_confirmation: "" } 47 | assert_not flash.empty? 48 | assert_redirected_to @user 49 | @user.reload 50 | assert_equal name, @user.name 51 | assert_equal email, @user.email 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Do not eager load code on boot. 10 | config.eager_load = false 11 | 12 | # Show full error reports 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 = true 18 | config.action_mailer.delivery_method = :test 19 | host = 'rails-tutorial-captainstack.c9users.io' 20 | config.action_mailer.default_url_options = { host: host } 21 | 22 | # Print deprecation notices to the Rails logger. 23 | config.active_support.deprecation = :log 24 | 25 | # Raise an error on page load if there are pending migrations. 26 | config.active_record.migration_error = :page_load 27 | 28 | # Debug mode disables concatenation and preprocessing of assets. 29 | # This option may cause significant delays in view rendering with a large 30 | # number of complex assets. 31 | config.assets.debug = true 32 | 33 | # Asset digests allow you to set far-future HTTP expiration dates on all assets, 34 | # yet still be able to expire them through the digest params. 35 | config.assets.digest = true 36 | 37 | # Adds additional error checking when serving assets at runtime. 38 | # Checks for improperly declared sprockets dependencies. 39 | # Raises helpful error messages. 40 | config.assets.raise_runtime_errors = true 41 | 42 | # Raises error for missing translations 43 | # config.action_view.raise_on_missing_translations = true 44 | end 45 | -------------------------------------------------------------------------------- /Guardfile: -------------------------------------------------------------------------------- 1 | # Defines the matching rules for Guard. 2 | guard :minitest, spring: true, all_on_start: false do 3 | watch(%r{^test/(.*)/?(.*)_test\.rb$}) 4 | watch('test/test_helper.rb') { 'test' } 5 | watch('config/routes.rb') { integration_tests } 6 | watch(%r{^app/models/(.*?)\.rb$}) do |matches| 7 | "test/models/#{matches[1]}_test.rb" 8 | end 9 | watch(%r{^app/controllers/(.*?)_controller\.rb$}) do |matches| 10 | resource_tests(matches[1]) 11 | end 12 | watch(%r{^app/views/([^/]*?)/.*\.html\.erb$}) do |matches| 13 | ["test/controllers/#{matches[1]}_controller_test.rb"] + 14 | integration_tests(matches[1]) 15 | end 16 | watch(%r{^app/helpers/(.*?)_helper\.rb$}) do |matches| 17 | integration_tests(matches[1]) 18 | end 19 | watch('app/views/layouts/application.html.erb') do 20 | 'test/integration/site_layout_test.rb' 21 | end 22 | watch('app/helpers/sessions_helper.rb') do 23 | integration_tests << 'test/helpers/sessions_helper_test.rb' 24 | end 25 | watch('app/controllers/sessions_controller.rb') do 26 | ['test/controllers/sessions_controller_test.rb', 27 | 'test/integration/users_login_test.rb'] 28 | end 29 | watch('app/controllers/account_activations_controller.rb') do 30 | 'test/integration/users_signup_test.rb' 31 | end 32 | watch(%r{app/views/users/*}) do 33 | resource_tests('users') + 34 | ['test/integration/microposts_interface_test.rb'] 35 | end 36 | end 37 | 38 | # Returns the integration tests corresponding to the given resource. 39 | def integration_tests(resource = :all) 40 | if resource == :all 41 | Dir["test/integration/*"] 42 | else 43 | Dir["test/integration/#{resource}_*.rb"] 44 | end 45 | end 46 | 47 | # Returns the controller tests corresponding to the given resource. 48 | def controller_test(resource) 49 | "test/controllers/#{resource}_controller_test.rb" 50 | end 51 | 52 | # Returns all tests for the given resource. 53 | def resource_tests(resource) 54 | integration_tests(resource) << controller_test(resource) 55 | end -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Rails.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 | # Do not eager load code on boot. This avoids loading your whole application 11 | # just for the purpose of running a single test. If you are using a tool that 12 | # preloads Rails for running tests, you may have to set it to true. 13 | config.eager_load = false 14 | 15 | # Configure static file server for tests with Cache-Control for performance. 16 | config.serve_static_files = true 17 | config.static_cache_control = 'public, max-age=3600' 18 | 19 | # Show full error reports and disable caching. 20 | config.consider_all_requests_local = true 21 | config.action_controller.perform_caching = false 22 | 23 | # Raise exceptions instead of rendering exception templates. 24 | config.action_dispatch.show_exceptions = false 25 | 26 | # Disable request forgery protection in test environment. 27 | config.action_controller.allow_forgery_protection = false 28 | 29 | # Tell Action Mailer not to deliver emails to the real world. 30 | # The :test delivery method accumulates sent emails in the 31 | # ActionMailer::Base.deliveries array. 32 | config.action_mailer.delivery_method = :test 33 | config.action_mailer.default_url_options = { host: 'rails-tutorial-captainstack.c9users.io' } 34 | 35 | # Randomize the order test cases are executed. 36 | config.active_support.test_order = :random 37 | 38 | # Print deprecation notices to the stderr. 39 | config.active_support.deprecation = :stderr 40 | 41 | # Raises error for missing translations 42 | # config.action_view.raise_on_missing_translations = true 43 | end 44 | -------------------------------------------------------------------------------- /test/integration/following_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class FollowingTest < ActionDispatch::IntegrationTest 4 | 5 | def setup 6 | @user = users(:michael) 7 | @other = users(:archer) 8 | log_in_as(@user) 9 | end 10 | 11 | test "following page" do 12 | get following_user_path(@user) 13 | assert_not @user.following.empty? 14 | assert_match @user.following.count.to_s, response.body 15 | @user.following.each do |user| 16 | assert_select "a[href=?]", user_path(user) 17 | end 18 | end 19 | 20 | test "followers page" do 21 | get followers_user_path(@user) 22 | assert_not @user.followers.empty? 23 | assert_match @user.followers.count.to_s, response.body 24 | @user.followers.each do |user| 25 | assert_select "a[href=?]", user_path(user) 26 | end 27 | end 28 | 29 | test "should follow a user the standard way" do 30 | assert_difference '@user.following.count', 1 do 31 | post relationships_path, followed_id: @other.id 32 | end 33 | end 34 | 35 | test "should follow a user with Ajax" do 36 | assert_difference '@user.following.count', 1 do 37 | xhr :post, relationships_path, followed_id: @other.id 38 | end 39 | end 40 | 41 | test "should unfollow a user the standard way" do 42 | @user.follow(@other) 43 | relationship = @user.active_relationships.find_by(followed_id: @other.id) 44 | assert_difference '@user.following.count', -1 do 45 | delete relationship_path(relationship) 46 | end 47 | end 48 | 49 | test "should unfollow a user with Ajax" do 50 | @user.follow(@other) 51 | relationship = @user.active_relationships.find_by(followed_id: @other.id) 52 | assert_difference '@user.following.count', -1 do 53 | xhr :delete, relationship_path(relationship) 54 | end 55 | end 56 | 57 | test "feed on Home page" do 58 | get root_path 59 | @user.feed.paginate(page: 1).each do |micropost| 60 | assert_match CGI.escapeHTML(micropost.content), response.body 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /app/controllers/users_controller.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | before_action :logged_in_user, only: [:index, :edit, :update, :destroy, :following, :followers] 3 | before_action :correct_user, only: [:edit, :update] 4 | before_action :admin_user, only: :destroy 5 | 6 | def index 7 | @users = User.where(activated: true).paginate(page: params[:page]) 8 | end 9 | 10 | def show 11 | @user = User.find(params[:id]) 12 | @microposts = @user.microposts.paginate(page: params[:page]) 13 | redirect_to root_url and return unless @user.activated? 14 | end 15 | 16 | def new 17 | @user = User.new 18 | end 19 | 20 | def create 21 | @user = User.new(user_params) 22 | if @user.save 23 | @user.send_activation_email 24 | flash[:info] = "Please check your email to activate your account." 25 | redirect_to root_url 26 | else 27 | render 'new' 28 | end 29 | end 30 | 31 | def edit 32 | @user = User.find(params[:id]) 33 | end 34 | 35 | def update 36 | @user = User.find(params[:id]) 37 | if @user.update_attributes(user_params) 38 | flash[:success] = "Profile updated" 39 | redirect_to @user 40 | else 41 | render 'edit' 42 | end 43 | end 44 | 45 | def destroy 46 | User.find(params[:id]).destroy 47 | flash[:success] = "User deleted" 48 | redirect_to root_url 49 | end 50 | 51 | def following 52 | @title = "Following" 53 | @user = User.find(params[:id]) 54 | @users = @user.following.paginate(page: params[:page]) 55 | render 'show_follow' 56 | end 57 | 58 | def followers 59 | @title = "Followers" 60 | @user = User.find(params[:id]) 61 | @users = @user.followers.paginate(page: params[:page]) 62 | render 'show_follow' 63 | end 64 | 65 | private 66 | 67 | def user_params 68 | params.require(:user).permit(:name, :email, :password, :password_confirmation, :admin) 69 | end 70 | 71 | # Before filters 72 | 73 | # Confirms the correct user. 74 | def correct_user 75 | @user = User.find(params[:id]) 76 | redirect_to(root_url) unless current_user?(@user) 77 | end 78 | 79 | # Confirms an admin user 80 | def admin_user 81 | redirect_to(root_url) unless current_user.admin? 82 | end 83 | end -------------------------------------------------------------------------------- /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 that you check this file into your version control system. 13 | 14 | ActiveRecord::Schema.define(version: 20160301020419) do 15 | 16 | create_table "microposts", force: :cascade do |t| 17 | t.text "content" 18 | t.integer "user_id" 19 | t.datetime "created_at", null: false 20 | t.datetime "updated_at", null: false 21 | t.string "picture" 22 | end 23 | 24 | add_index "microposts", ["user_id", "created_at"], name: "index_microposts_on_user_id_and_created_at" 25 | add_index "microposts", ["user_id"], name: "index_microposts_on_user_id" 26 | 27 | create_table "relationships", force: :cascade do |t| 28 | t.integer "follower_id" 29 | t.integer "followed_id" 30 | t.datetime "created_at", null: false 31 | t.datetime "updated_at", null: false 32 | end 33 | 34 | add_index "relationships", ["followed_id"], name: "index_relationships_on_followed_id" 35 | add_index "relationships", ["follower_id", "followed_id"], name: "index_relationships_on_follower_id_and_followed_id", unique: true 36 | add_index "relationships", ["follower_id"], name: "index_relationships_on_follower_id" 37 | 38 | create_table "users", force: :cascade do |t| 39 | t.string "name" 40 | t.string "email" 41 | t.datetime "created_at", null: false 42 | t.datetime "updated_at", null: false 43 | t.string "password_digest" 44 | t.string "remember_digest" 45 | t.boolean "admin" 46 | t.string "activation_digest" 47 | t.boolean "activated", default: false 48 | t.datetime "activated_at" 49 | t.string "reset_digest" 50 | t.datetime "reset_sent_at" 51 | end 52 | 53 | add_index "users", ["email"], name: "index_users_on_email", unique: true 54 | 55 | end 56 | -------------------------------------------------------------------------------- /test/controllers/users_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UsersControllerTest < ActionController::TestCase 4 | 5 | def setup 6 | @user = users(:michael) 7 | @other_user = users(:archer) 8 | end 9 | 10 | test "should redirect index when not logged in" do 11 | get :index 12 | assert_redirected_to login_url 13 | end 14 | 15 | test "should get new" do 16 | get :new 17 | assert_response :success 18 | end 19 | 20 | test "should redirect edit when not logged in" do 21 | get :edit, id: @user 22 | assert_not flash.empty? 23 | assert_redirected_to login_url 24 | end 25 | 26 | test "should redirect update when not logged in" do 27 | patch :update, id: @user, user: { name: @user.name, email: @user.email } 28 | assert_not flash.empty? 29 | assert_redirected_to login_url 30 | end 31 | 32 | test "should redirect edit when logged in as wrong user" do 33 | log_in_as(@other_user) 34 | get :edit, id: @user 35 | assert flash.empty? 36 | assert_redirected_to root_url 37 | end 38 | 39 | test "should redirect update when logged in as wrong user" do 40 | log_in_as(@other_user) 41 | patch :update, id: @user, user: { name: @user.name, email: @user.email } 42 | assert flash.empty? 43 | assert_redirected_to root_url 44 | end 45 | 46 | test "should not allow the admin attribute to be edited via the web" do 47 | log_in_as(@other_user) 48 | assert_not @other_user.admin? 49 | patch :update, id: @other_user, user: { password: @other_user.password, 50 | password_confirmation: @other_user.password, 51 | admin: 1 } 52 | assert_not @other_user.reload.admin? 53 | end 54 | 55 | test "should redirect destroy when not logged in" do 56 | assert_no_difference 'User.count' do 57 | delete :destroy, id: @user 58 | end 59 | assert_redirected_to login_url 60 | end 61 | 62 | test "should redirect destroy when logged in as a non-admin" do 63 | log_in_as(@other_user) 64 | assert_no_difference 'User.count' do 65 | delete :destroy, id: @user 66 | end 67 | assert_redirected_to root_url 68 | end 69 | 70 | test "should redirect following when not logged in" do 71 | get :following, id: @user 72 | assert_redirected_to login_url 73 | end 74 | 75 | test "should redirect followers when not logged in" do 76 | get :followers, id: @user 77 | assert_redirected_to login_url 78 | end 79 | end 80 | -------------------------------------------------------------------------------- /test/integration/password_resets_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class PasswordResetsTest < ActionDispatch::IntegrationTest 4 | 5 | def setup 6 | ActionMailer::Base.deliveries.clear 7 | @user = users(:michael) 8 | end 9 | 10 | test "password resets" do 11 | get new_password_reset_path 12 | assert_template 'password_resets/new' 13 | # Invalid email 14 | post password_resets_path, password_reset: { email: "" } 15 | assert_not flash.empty? 16 | assert_template 'password_resets/new' 17 | # Valid email 18 | post password_resets_path, password_reset: { email: @user.email } 19 | assert_not_equal @user.reset_digest, @user.reload.reset_digest 20 | assert_equal 1, ActionMailer::Base.deliveries.size 21 | assert_not flash.empty? 22 | assert_redirected_to root_url 23 | # Password reset form 24 | user = assigns(:user) 25 | # Wrong email 26 | get edit_password_reset_path(user.reset_token, email: "") 27 | assert_redirected_to root_url 28 | # Inactive user 29 | user.toggle!(:activated) 30 | get edit_password_reset_path(user.reset_token, email: user.email) 31 | assert_redirected_to root_url 32 | user.toggle!(:activated) 33 | # Right email, wrong token 34 | get edit_password_reset_path('wrong token', email: user.email) 35 | assert_redirected_to root_url 36 | # Right email, right token 37 | get edit_password_reset_path(user.reset_token, email: user.email) 38 | assert_template 'password_resets/edit' 39 | assert_select "input[name=email][type=hidden][value=?]", user.email 40 | # Invalid password & confirmation 41 | patch password_reset_path(user.reset_token), 42 | email: user.email, 43 | user: { password: "foobaz", 44 | password_confirmation: "barquux" } 45 | assert_select 'div#error_explanation' 46 | # Empty password 47 | patch password_reset_path(user.reset_token), 48 | email: user.email, 49 | user: { password: "", 50 | password_confirmation: "" } 51 | assert_select 'div#error_explanation' 52 | # Valid password & confirmation 53 | patch password_reset_path(user.reset_token), 54 | email: user.email, 55 | user: { password: "foobaz", 56 | password_confirmation: "foobaz" } 57 | assert is_logged_in? 58 | assert_not flash.empty? 59 | assert_redirected_to user 60 | end 61 | 62 | test "expired token" do 63 | get new_password_reset_path 64 | post password_resets_path, password_reset: { email: @user.email } 65 | 66 | @user = assigns(:user) 67 | @user.update_attribute(:reset_sent_at, 3.hours.ago) 68 | patch password_reset_path(@user.reset_token), 69 | email: @user.email, 70 | user: { password: "foobar", 71 | password_confirmation: "foobar" } 72 | assert_response :redirect 73 | follow_redirect! 74 | assert_match /expired/i, response.body 75 | end 76 | end 77 | -------------------------------------------------------------------------------- /test/models/user_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UserTest < ActiveSupport::TestCase 4 | 5 | def setup 6 | @user = User.new(name: "Example User", email: "user@example.com", password: "foobar", password_confirmation: "foobar") 7 | end 8 | 9 | test "should be valid" do 10 | assert @user.valid? 11 | end 12 | 13 | test "name should be present" do 14 | @user.name = " " 15 | assert_not @user.valid? 16 | end 17 | 18 | test "name should not be too long" do 19 | @user.name = "a" * 51 20 | assert_not @user.valid? 21 | end 22 | 23 | test "email should not be too long" do 24 | @user.email = "a" * 244 + "@example.com" 25 | assert_not @user.valid? 26 | end 27 | 28 | test "email validation should accept valid addresses" do 29 | valid_addresses = %w[user@example.com USER@foo.COM A_US-ER@foo.bar.org first.last@foo.jp alice+bob@baz.cn] 30 | valid_addresses.each do |valid_address| 31 | @user.email = valid_address 32 | assert @user.valid?, "#{valid_address.inspect} should be valid" 33 | end 34 | end 35 | 36 | test "email validation should reject invalid addresses" do 37 | invalid_addresses = %w[user@example,com user_at_foo.org user.name@example. foo@bar_baz.com foo@bar+baz.com] 38 | invalid_addresses.each do |invalid_address| 39 | @user.email = invalid_address 40 | assert_not @user.valid?, "#{invalid_address.inspect} should be invalid" 41 | end 42 | end 43 | 44 | test "email addresses should be unique" do 45 | duplicate_user = @user.dup 46 | duplicate_user.email = @user.email.upcase 47 | @user.save 48 | assert_not duplicate_user.valid? 49 | end 50 | 51 | test "password should be present (nonblank)" do 52 | @user.password = @user.password_confirmation = " " * 6 53 | assert_not @user.valid? 54 | end 55 | 56 | test "email addresses should be saved as lower-case" do 57 | mixed_case_email = "Foo@ExAMPle.CoM" 58 | @user.email = mixed_case_email 59 | @user.save 60 | assert_equal mixed_case_email.downcase, @user.reload.email 61 | end 62 | 63 | test "password should have a minimum length" do 64 | @user.password = @user.password_confirmation = "a" * 5 65 | assert_not @user.valid? 66 | end 67 | 68 | test "authenticated? should return false for a user with nil digest" do 69 | assert_not @user.authenticated?(:remember, '') 70 | end 71 | 72 | test "associated microposts should be destroyed" do 73 | @user.save 74 | @user.microposts.create!(content: "Lorem ipsum") 75 | assert_difference 'Micropost.count', -1 do 76 | @user.destroy 77 | end 78 | end 79 | 80 | test "should follow and unfollow a user" do 81 | michael = users(:michael) 82 | archer = users(:archer) 83 | assert_not michael.following?(archer) 84 | michael.follow(archer) 85 | assert michael.following?(archer) 86 | assert archer.followers.include?(michael) 87 | michael.unfollow(archer) 88 | assert_not michael.following?(archer) 89 | end 90 | 91 | test "feed should have the right posts" do 92 | michael = users(:michael) 93 | archer = users(:archer) 94 | lana = users(:lana) 95 | # Posts from followed user 96 | lana.microposts.each do |post_following| 97 | assert michael.feed.include?(post_following) 98 | end 99 | # Posts from self 100 | michael.microposts.each do |post_self| 101 | assert michael.feed.include?(post_self) 102 | end 103 | # Posts from unfollowed user 104 | archer.microposts.each do |post_unfollowed| 105 | assert_not michael.feed.include?(post_unfollowed) 106 | end 107 | end 108 | end 109 | -------------------------------------------------------------------------------- /config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # Code is not reloaded between requests. 5 | config.cache_classes = true 6 | 7 | # Eager load code on boot. This eager loads most of Rails and 8 | # your application in memory, allowing both threaded web servers 9 | # and those relying on copy on write to perform better. 10 | # Rake tasks automatically ignore this option for performance. 11 | config.eager_load = true 12 | 13 | # Full error reports are disabled and caching is turned on. 14 | config.consider_all_requests_local = false 15 | config.action_controller.perform_caching = true 16 | 17 | # Enable Rack::Cache to put a simple HTTP cache in front of your application 18 | # Add `rack-cache` to your Gemfile before enabling this. 19 | # For large-scale production use, consider using a caching reverse proxy like 20 | # NGINX, varnish or squid. 21 | # config.action_dispatch.rack_cache = true 22 | 23 | # Disable serving static files from the `/public` folder by default since 24 | # Apache or NGINX already handles this. 25 | config.serve_static_files = ENV['RAILS_SERVE_STATIC_FILES'].present? 26 | 27 | # Compress JavaScripts and CSS. 28 | config.assets.js_compressor = :uglifier 29 | # config.assets.css_compressor = :sass 30 | 31 | # Do not fallback to assets pipeline if a precompiled asset is missed. 32 | config.assets.compile = false 33 | 34 | # Asset digests allow you to set far-future HTTP expiration dates on all assets, 35 | # yet still be able to expire them through the digest params. 36 | config.assets.digest = true 37 | 38 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb 39 | 40 | # Specifies the header that your server uses for sending files. 41 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache 42 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX 43 | 44 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 45 | config.force_ssl = true 46 | 47 | # Use the lowest log level to ensure availability of diagnostic information 48 | # when problems arise. 49 | config.log_level = :debug 50 | 51 | # Prepend all log lines with the following tags. 52 | # config.log_tags = [ :subdomain, :uuid ] 53 | 54 | # Use a different logger for distributed setups. 55 | # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) 56 | 57 | # Use a different cache store in production. 58 | # config.cache_store = :mem_cache_store 59 | 60 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 61 | # config.action_controller.asset_host = 'http://assets.example.com' 62 | 63 | # Ignore bad email addresses and do not raise email delivery errors. 64 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 65 | config.action_mailer.raise_delivery_errors = true 66 | config.action_mailer.delivery_method = :smtp 67 | host = '.herokuapp.com' 68 | config.action_mailer.default_url_options = { host: 'pacific-spire-36298.herokuapp.com' } 69 | ActionMailer::Base.smtp_settings = { 70 | :address => 'smtp.sendgrid.net', 71 | :port => '587', 72 | :authentication => :plain, 73 | :user_name => ENV['SENDGRID_USERNAME'], 74 | :password => ENV['SENDGRID_PASSWORD'], 75 | :domain => 'heroku.com', 76 | :enable_starttls_auto => true 77 | } 78 | 79 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 80 | # the I18n.default_locale when a translation cannot be found). 81 | config.i18n.fallbacks = true 82 | 83 | # Send deprecation notices to registered listeners. 84 | config.active_support.deprecation = :notify 85 | 86 | # Use default logging formatter so that PID and timestamp are not suppressed. 87 | config.log_formatter = ::Logger::Formatter.new 88 | 89 | # Do not dump schema after migrations. 90 | config.active_record.dump_schema_after_migration = false 91 | end 92 | -------------------------------------------------------------------------------- /app/models/user.rb: -------------------------------------------------------------------------------- 1 | class User < ActiveRecord::Base 2 | has_many :microposts, dependent: :destroy 3 | has_many :active_relationships, class_name: "Relationship", 4 | foreign_key: "follower_id", 5 | dependent: :destroy 6 | has_many :passive_relationships, class_name: "Relationship", 7 | foreign_key: "followed_id", 8 | dependent: :destroy 9 | has_many :following, through: :active_relationships, source: :followed 10 | has_many :followers, through: :passive_relationships, source: :follower 11 | attr_accessor :remember_token, :activation_token, :reset_token 12 | before_save :downcase_email 13 | before_create :create_activation_digest 14 | validates :name, presence: true, length: { maximum: 50 } 15 | VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i 16 | validates :email, presence: true, length: { maximum: 255 }, 17 | format: { with: VALID_EMAIL_REGEX }, 18 | uniqueness: { case_sensitive: false } 19 | has_secure_password 20 | validates :password, presence: true, length: { minimum: 6 }, allow_nil: true 21 | 22 | class << self 23 | # Returns the hash digest of the given string. 24 | def digest(string) 25 | cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : 26 | BCrypt::Engine.cost 27 | BCrypt::Password.create(string, cost: cost) 28 | end 29 | 30 | # Returns a random token. 31 | def new_token 32 | SecureRandom.urlsafe_base64 33 | end 34 | end 35 | 36 | def User.digest(string) 37 | cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost 38 | BCrypt::Password.create(string, cost: cost) 39 | end 40 | 41 | # Remembers a user in the database for use in persistent sessions. 42 | def remember 43 | self.remember_token = User.new_token 44 | update_attribute(:remember_digest, User.digest(remember_token)) 45 | end 46 | 47 | # Returns true if the given token matches the digest. 48 | def authenticated?(attribute, token) 49 | digest = send("#{attribute}_digest") 50 | return false if digest.nil? 51 | BCrypt::Password.new(digest).is_password?(token) 52 | end 53 | 54 | # Forgets a user. 55 | def forget 56 | update_attribute(:remember_digest, nil) 57 | end 58 | 59 | # Activates an account. 60 | def activate 61 | update_columns(activated: true, activated_at: Time.zone.now) 62 | end 63 | 64 | # Sends activation email. 65 | def send_activation_email 66 | UserMailer.account_activation(self).deliver_now 67 | end 68 | 69 | # Sets the password reset attributes. 70 | def create_reset_digest 71 | self.reset_token = User.new_token 72 | update_columns(reset_digest: User.digest(reset_token), reset_sent_at: Time.zone.now) 73 | end 74 | 75 | # Sends password reset email. 76 | def send_password_reset_email 77 | UserMailer.password_reset(self).deliver_now 78 | end 79 | 80 | # Returns true if a password reset has expired. 81 | def password_reset_expired? 82 | reset_sent_at < 2.hours.ago 83 | end 84 | 85 | # Returns a user's status feed. 86 | def feed 87 | following_ids = "SELECT followed_id FROM relationships 88 | WHERE follower_id = :user_id" 89 | Micropost.where("user_id IN (#{following_ids}) 90 | OR user_id = :user_id", user_id: id) 91 | end 92 | 93 | # Follows a user. 94 | def follow(other_user) 95 | active_relationships.create(followed_id: other_user.id) 96 | end 97 | 98 | # Unfollows a user. 99 | def unfollow(other_user) 100 | active_relationships.find_by(followed_id: other_user.id).destroy 101 | end 102 | 103 | # Returns true if the current user is following the other user. 104 | def following?(other_user) 105 | following.include?(other_user) 106 | end 107 | 108 | private 109 | 110 | # Converts email to all lower-case. 111 | def downcase_email 112 | self.email = email.downcase 113 | end 114 | 115 | # Creates and assigns the activation token and digest. 116 | def create_activation_digest 117 | self.activation_token = User.new_token 118 | self.activation_digest = User.digest(activation_token) 119 | end 120 | end 121 | -------------------------------------------------------------------------------- /app/assets/stylesheets/custom.css.scss: -------------------------------------------------------------------------------- 1 | @import "bootstrap-sprockets"; 2 | @import "bootstrap"; 3 | 4 | /* mixins, variables, etc. */ 5 | 6 | $gray-medium-light: #eaeaea; 7 | 8 | @mixin box_sizing { 9 | -mox-box-sizing: border-box; 10 | -webkit-box-sizing: border-box; 11 | box-sizing: border-box; 12 | } 13 | 14 | /* universal */ 15 | 16 | body { 17 | padding-top: 60px; 18 | } 19 | 20 | section { 21 | overflow: auto; 22 | } 23 | 24 | textarea { 25 | resize: vertical; 26 | } 27 | 28 | .center { 29 | text-align: center; 30 | h1 { 31 | margin-bottom: 10px; 32 | } 33 | } 34 | 35 | /* typography */ 36 | 37 | h1, h2, h3, h4, h5, h6 { 38 | line-height: 1; 39 | } 40 | 41 | h1 { 42 | font-size: 3em; 43 | letter-spacing: -2px; 44 | margin-bottom: 30px; 45 | text-align: center; 46 | } 47 | 48 | h2 { 49 | font-size: 1.2em; 50 | letter-spacing: -1px; 51 | margin-bottom: 30px; 52 | text-align: center; 53 | font-weight: normal; 54 | color: $gray-light; 55 | } 56 | 57 | p { 58 | font-size: 1.1em; 59 | line-height: 1.7em; 60 | } 61 | 62 | 63 | /* header */ 64 | 65 | #logo { 66 | float: left; 67 | margin-right: 10px; 68 | font-size: 1.7em; 69 | color: white; 70 | text-transform: uppercase; 71 | letter-spacing: -1px; 72 | padding-top: 9px; 73 | font-weight: bold; 74 | &:hover { 75 | color: white; 76 | text-decoration: none; 77 | } 78 | } 79 | 80 | /* footer */ 81 | 82 | footer { 83 | margin-top: 45px; 84 | padding-top: 5px; 85 | border-top: 1px solid $gray-medium-light; 86 | color: $gray-light; 87 | a { 88 | color: $gray; 89 | &:hover { 90 | color: $gray-darker; 91 | } 92 | } 93 | small { 94 | float: left; 95 | } 96 | ul { 97 | float: right; 98 | list-style: none; 99 | li { 100 | float: left; 101 | margin-left: 15px; 102 | } 103 | } 104 | } 105 | 106 | /* miscellaneous */ 107 | 108 | .debug_dump { 109 | clear: both; 110 | float: left; 111 | width: 100%; 112 | margin-top: 45px; 113 | @include box_sizing; 114 | } 115 | 116 | /* sidebar */ 117 | 118 | aside { 119 | section.user_info { 120 | margin-top: 20px; 121 | } 122 | section { 123 | padding: 10px 0; 124 | margin-top: 20px; 125 | &:first-child { 126 | border: 0; 127 | padding-top: 0; 128 | } 129 | span { 130 | display: block; 131 | margin-bottom: 3px; 132 | line-height: 1; 133 | } 134 | h1 { 135 | font-size: 1.4em; 136 | text-align: left; 137 | letter-spacing: -1px; 138 | margin-bottom: 3px; 139 | margin-top: 0px; 140 | } 141 | } 142 | } 143 | 144 | .gravatar { 145 | float: left; 146 | margin-right: 10px; 147 | } 148 | 149 | .gravatar_edit { 150 | margin-top: 15px; 151 | } 152 | 153 | .stats { 154 | overflow: auto; 155 | margin-top: 0; 156 | padding: 0; 157 | a { 158 | float: left; 159 | padding: 0 10px; 160 | border-left: 1px solid $gray-lighter; 161 | color: gray; 162 | &:first-child { 163 | padding-left: 0; 164 | border: 0; 165 | } 166 | &:hover { 167 | text-decoration: none; 168 | color: blue; 169 | } 170 | } 171 | strong { 172 | display: block; 173 | } 174 | } 175 | 176 | .user_avatars { 177 | overflow: auto; 178 | margin-top: 10px; 179 | .gravatar { 180 | margin: 1px 1px; 181 | } 182 | a { 183 | padding: 0; 184 | } 185 | } 186 | 187 | .users.follow { 188 | padding: 0; 189 | } 190 | 191 | /* forms */ 192 | 193 | input, textarea, select, .uneditable-input { 194 | border: 1px solid #bbb; 195 | width: 100%; 196 | margin-bottom: 15px; 197 | @include box_sizing; 198 | } 199 | 200 | input { 201 | height: auto !important; 202 | } 203 | 204 | #error_explanation { 205 | color: red; 206 | ul { 207 | color: red; 208 | margin: 0 0 30px 0; 209 | } 210 | } 211 | 212 | .field_with_errors { 213 | @extend .has-error; 214 | .form-control { 215 | color: $state-danger-text; 216 | } 217 | } 218 | 219 | .checkbox { 220 | margin-top: -10px; 221 | margin-bottom: 10px; 222 | span { 223 | margin-left: 20px; 224 | font-weight: normal; 225 | } 226 | } 227 | 228 | #session_remember_me { 229 | width: auto; 230 | margin-left: 0; 231 | } 232 | 233 | /* Users index */ 234 | 235 | .users { 236 | list-style: none; 237 | margin: 0; 238 | li { 239 | overflow: auto; 240 | padding: 10px 0; 241 | border-bottom: 1px solid $gray-lighter; 242 | } 243 | } 244 | 245 | /* microposts */ 246 | 247 | .microposts { 248 | list-style: none; 249 | padding: 0; 250 | li { 251 | padding: 10px 0; 252 | border-top: 1px solid #e8e8e8; 253 | } 254 | .user { 255 | margin-top: 5em; 256 | padding-top: 0; 257 | } 258 | .content { 259 | display: block; 260 | margin-left: 60px; 261 | img { 262 | display: block; 263 | padding: 5px 0; 264 | } 265 | } 266 | .timestamp { 267 | color: $gray-light; 268 | display: block; 269 | margin-left: 60px; 270 | } 271 | .gravatar { 272 | float: left; 273 | margin-right: 10px; 274 | margin-top: 5px; 275 | } 276 | } 277 | 278 | aside { 279 | textarea { 280 | height: 100px; 281 | margin-bottom: 5px; 282 | } 283 | } 284 | 285 | span.picture { 286 | margin-top: 10px; 287 | input { 288 | border: 0; 289 | } 290 | } -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | CFPropertyList (2.3.2) 5 | actionmailer (4.2.2) 6 | actionpack (= 4.2.2) 7 | actionview (= 4.2.2) 8 | activejob (= 4.2.2) 9 | mail (~> 2.5, >= 2.5.4) 10 | rails-dom-testing (~> 1.0, >= 1.0.5) 11 | actionpack (4.2.2) 12 | actionview (= 4.2.2) 13 | activesupport (= 4.2.2) 14 | rack (~> 1.6) 15 | rack-test (~> 0.6.2) 16 | rails-dom-testing (~> 1.0, >= 1.0.5) 17 | rails-html-sanitizer (~> 1.0, >= 1.0.1) 18 | actionview (4.2.2) 19 | activesupport (= 4.2.2) 20 | builder (~> 3.1) 21 | erubis (~> 2.7.0) 22 | rails-dom-testing (~> 1.0, >= 1.0.5) 23 | rails-html-sanitizer (~> 1.0, >= 1.0.1) 24 | activejob (4.2.2) 25 | activesupport (= 4.2.2) 26 | globalid (>= 0.3.0) 27 | activemodel (4.2.2) 28 | activesupport (= 4.2.2) 29 | builder (~> 3.1) 30 | activerecord (4.2.2) 31 | activemodel (= 4.2.2) 32 | activesupport (= 4.2.2) 33 | arel (~> 6.0) 34 | activesupport (4.2.2) 35 | i18n (~> 0.7) 36 | json (~> 1.7, >= 1.7.7) 37 | minitest (~> 5.1) 38 | thread_safe (~> 0.3, >= 0.3.4) 39 | tzinfo (~> 1.1) 40 | ansi (1.5.0) 41 | arel (6.0.3) 42 | bcrypt (3.1.7) 43 | binding_of_caller (0.7.3.pre1) 44 | debug_inspector (>= 0.0.1) 45 | bootstrap-sass (3.2.0.0) 46 | sass (~> 3.2) 47 | bootstrap-will_paginate (0.0.10) 48 | will_paginate 49 | builder (3.2.2) 50 | byebug (3.4.0) 51 | columnize (~> 0.8) 52 | debugger-linecache (~> 1.2) 53 | slop (~> 3.6) 54 | carrierwave (0.10.0) 55 | activemodel (>= 3.2.0) 56 | activesupport (>= 3.2.0) 57 | json (>= 1.7) 58 | mime-types (>= 1.16) 59 | coderay (1.1.0) 60 | coffee-rails (4.1.0) 61 | coffee-script (>= 2.2.0) 62 | railties (>= 4.0.0, < 5.0) 63 | coffee-script (2.4.1) 64 | coffee-script-source 65 | execjs 66 | coffee-script-source (1.10.0) 67 | columnize (0.9.0) 68 | concurrent-ruby (1.0.0) 69 | debug_inspector (0.0.2) 70 | debugger-linecache (1.2.0) 71 | erubis (2.7.0) 72 | excon (0.46.0) 73 | execjs (2.6.0) 74 | faker (1.4.2) 75 | i18n (~> 0.5) 76 | ffi (1.9.10) 77 | fission (0.5.0) 78 | CFPropertyList (~> 2.2) 79 | fog (1.36.0) 80 | fog-aliyun (>= 0.1.0) 81 | fog-atmos 82 | fog-aws (>= 0.6.0) 83 | fog-brightbox (~> 0.4) 84 | fog-core (~> 1.32) 85 | fog-dynect (~> 0.0.2) 86 | fog-ecloud (~> 0.1) 87 | fog-google (<= 0.1.0) 88 | fog-json 89 | fog-local 90 | fog-powerdns (>= 0.1.1) 91 | fog-profitbricks 92 | fog-radosgw (>= 0.0.2) 93 | fog-riakcs 94 | fog-sakuracloud (>= 0.0.4) 95 | fog-serverlove 96 | fog-softlayer 97 | fog-storm_on_demand 98 | fog-terremark 99 | fog-vmfusion 100 | fog-voxel 101 | fog-xenserver 102 | fog-xml (~> 0.1.1) 103 | ipaddress (~> 0.5) 104 | nokogiri (~> 1.5, >= 1.5.11) 105 | fog-aliyun (0.1.0) 106 | fog-core (~> 1.27) 107 | fog-json (~> 1.0) 108 | ipaddress (~> 0.8) 109 | xml-simple (~> 1.1) 110 | fog-atmos (0.1.0) 111 | fog-core 112 | fog-xml 113 | fog-aws (0.8.1) 114 | fog-core (~> 1.27) 115 | fog-json (~> 1.0) 116 | fog-xml (~> 0.1) 117 | ipaddress (~> 0.8) 118 | fog-brightbox (0.10.1) 119 | fog-core (~> 1.22) 120 | fog-json 121 | inflecto (~> 0.0.2) 122 | fog-core (1.36.0) 123 | builder 124 | excon (~> 0.45) 125 | formatador (~> 0.2) 126 | fog-dynect (0.0.2) 127 | fog-core 128 | fog-json 129 | fog-xml 130 | fog-ecloud (0.3.0) 131 | fog-core 132 | fog-xml 133 | fog-google (0.1.0) 134 | fog-core 135 | fog-json 136 | fog-xml 137 | fog-json (1.0.2) 138 | fog-core (~> 1.0) 139 | multi_json (~> 1.10) 140 | fog-local (0.2.1) 141 | fog-core (~> 1.27) 142 | fog-powerdns (0.1.1) 143 | fog-core (~> 1.27) 144 | fog-json (~> 1.0) 145 | fog-xml (~> 0.1) 146 | fog-profitbricks (0.0.5) 147 | fog-core 148 | fog-xml 149 | nokogiri 150 | fog-radosgw (0.0.5) 151 | fog-core (>= 1.21.0) 152 | fog-json 153 | fog-xml (>= 0.0.1) 154 | fog-riakcs (0.1.0) 155 | fog-core 156 | fog-json 157 | fog-xml 158 | fog-sakuracloud (1.7.5) 159 | fog-core 160 | fog-json 161 | fog-serverlove (0.1.2) 162 | fog-core 163 | fog-json 164 | fog-softlayer (1.1.0) 165 | fog-core 166 | fog-json 167 | fog-storm_on_demand (0.1.1) 168 | fog-core 169 | fog-json 170 | fog-terremark (0.1.0) 171 | fog-core 172 | fog-xml 173 | fog-vmfusion (0.1.0) 174 | fission 175 | fog-core 176 | fog-voxel (0.1.0) 177 | fog-core 178 | fog-xml 179 | fog-xenserver (0.2.3) 180 | fog-core 181 | fog-xml 182 | fog-xml (0.1.2) 183 | fog-core 184 | nokogiri (~> 1.5, >= 1.5.11) 185 | formatador (0.2.5) 186 | globalid (0.3.6) 187 | activesupport (>= 4.1.0) 188 | guard (2.13.0) 189 | formatador (>= 0.2.4) 190 | listen (>= 2.7, <= 4.0) 191 | lumberjack (~> 1.0) 192 | nenv (~> 0.1) 193 | notiffany (~> 0.0) 194 | pry (>= 0.9.12) 195 | shellany (~> 0.0) 196 | thor (>= 0.18.1) 197 | guard-minitest (2.3.1) 198 | guard (~> 2.0) 199 | minitest (>= 3.0) 200 | i18n (0.7.0) 201 | inflecto (0.0.2) 202 | ipaddress (0.8.3) 203 | jbuilder (2.2.3) 204 | activesupport (>= 3.0.0, < 5) 205 | multi_json (~> 1.2) 206 | jquery-rails (4.0.3) 207 | rails-dom-testing (~> 1.0) 208 | railties (>= 4.2.0) 209 | thor (>= 0.14, < 2.0) 210 | json (1.8.3) 211 | listen (3.0.6) 212 | rb-fsevent (>= 0.9.3) 213 | rb-inotify (>= 0.9.7) 214 | loofah (2.0.3) 215 | nokogiri (>= 1.5.9) 216 | lumberjack (1.0.10) 217 | mail (2.6.3) 218 | mime-types (>= 1.16, < 3) 219 | method_source (0.8.2) 220 | mime-types (2.99) 221 | mini_backtrace (0.1.3) 222 | minitest (> 1.2.0) 223 | rails (>= 2.3.3) 224 | mini_magick (3.8.0) 225 | subexec (~> 0.2.1) 226 | mini_portile2 (2.0.0) 227 | minitest (5.8.4) 228 | minitest-reporters (1.0.5) 229 | ansi 230 | builder 231 | minitest (>= 5.0) 232 | ruby-progressbar 233 | multi_json (1.11.2) 234 | nenv (0.3.0) 235 | nokogiri (1.6.7.2) 236 | mini_portile2 (~> 2.0.0.rc2) 237 | notiffany (0.0.8) 238 | nenv (~> 0.1) 239 | shellany (~> 0.0) 240 | pg (0.17.1) 241 | pry (0.10.3) 242 | coderay (~> 1.1.0) 243 | method_source (~> 0.8.1) 244 | slop (~> 3.4) 245 | puma (2.11.1) 246 | rack (>= 1.1, < 2.0) 247 | rack (1.6.4) 248 | rack-test (0.6.3) 249 | rack (>= 1.0) 250 | rails (4.2.2) 251 | actionmailer (= 4.2.2) 252 | actionpack (= 4.2.2) 253 | actionview (= 4.2.2) 254 | activejob (= 4.2.2) 255 | activemodel (= 4.2.2) 256 | activerecord (= 4.2.2) 257 | activesupport (= 4.2.2) 258 | bundler (>= 1.3.0, < 2.0) 259 | railties (= 4.2.2) 260 | sprockets-rails 261 | rails-deprecated_sanitizer (1.0.3) 262 | activesupport (>= 4.2.0.alpha) 263 | rails-dom-testing (1.0.7) 264 | activesupport (>= 4.2.0.beta, < 5.0) 265 | nokogiri (~> 1.6.0) 266 | rails-deprecated_sanitizer (>= 1.0.1) 267 | rails-html-sanitizer (1.0.3) 268 | loofah (~> 2.0) 269 | rails_12factor (0.0.2) 270 | rails_serve_static_assets 271 | rails_stdout_logging 272 | rails_serve_static_assets (0.0.5) 273 | rails_stdout_logging (0.0.4) 274 | railties (4.2.2) 275 | actionpack (= 4.2.2) 276 | activesupport (= 4.2.2) 277 | rake (>= 0.8.7) 278 | thor (>= 0.18.1, < 2.0) 279 | rake (10.5.0) 280 | rb-fsevent (0.9.7) 281 | rb-inotify (0.9.7) 282 | ffi (>= 0.5.0) 283 | rdoc (4.2.2) 284 | json (~> 1.4) 285 | ruby-progressbar (1.7.5) 286 | sass (3.4.21) 287 | sass-rails (5.0.2) 288 | railties (>= 4.0.0, < 5.0) 289 | sass (~> 3.1) 290 | sprockets (>= 2.8, < 4.0) 291 | sprockets-rails (>= 2.0, < 4.0) 292 | tilt (~> 1.1) 293 | sdoc (0.4.0) 294 | json (~> 1.8) 295 | rdoc (~> 4.0, < 5.0) 296 | shellany (0.0.1) 297 | slop (3.6.0) 298 | spring (1.1.3) 299 | sprockets (3.5.2) 300 | concurrent-ruby (~> 1.0) 301 | rack (> 1, < 3) 302 | sprockets-rails (3.0.1) 303 | actionpack (>= 4.0) 304 | activesupport (>= 4.0) 305 | sprockets (>= 3.0.0) 306 | sqlite3 (1.3.9) 307 | subexec (0.2.3) 308 | thor (0.19.1) 309 | thread_safe (0.3.5) 310 | tilt (1.4.1) 311 | turbolinks (2.3.0) 312 | coffee-rails 313 | tzinfo (1.2.2) 314 | thread_safe (~> 0.1) 315 | uglifier (2.5.3) 316 | execjs (>= 0.3.0) 317 | json (>= 1.8.0) 318 | web-console (2.0.0.beta3) 319 | activemodel (~> 4.0) 320 | binding_of_caller (= 0.7.3.pre1) 321 | railties (~> 4.0) 322 | sprockets-rails (>= 2.0, < 4.0) 323 | will_paginate (3.0.7) 324 | xml-simple (1.1.5) 325 | 326 | PLATFORMS 327 | ruby 328 | 329 | DEPENDENCIES 330 | bcrypt (= 3.1.7) 331 | bootstrap-sass (= 3.2.0.0) 332 | bootstrap-will_paginate (= 0.0.10) 333 | byebug (= 3.4.0) 334 | carrierwave (= 0.10.0) 335 | coffee-rails (= 4.1.0) 336 | faker (= 1.4.2) 337 | fog (= 1.36.0) 338 | guard-minitest (= 2.3.1) 339 | jbuilder (= 2.2.3) 340 | jquery-rails (= 4.0.3) 341 | mini_backtrace (= 0.1.3) 342 | mini_magick (= 3.8.0) 343 | minitest-reporters (= 1.0.5) 344 | pg (= 0.17.1) 345 | puma (= 2.11.1) 346 | rails (= 4.2.2) 347 | rails_12factor (= 0.0.2) 348 | sass-rails (= 5.0.2) 349 | sdoc (= 0.4.0) 350 | spring (= 1.1.3) 351 | sqlite3 (= 1.3.9) 352 | turbolinks (= 2.3.0) 353 | uglifier (= 2.5.3) 354 | web-console (= 2.0.0.beta3) 355 | will_paginate (= 3.0.7) 356 | --------------------------------------------------------------------------------