diff --git a/.gitignore b/.gitignore index 48fb168..ca7f410 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ # or operating system, you probably want to add a global ignore instead: # git config --global core.excludesfile '~/.gitignore_global' +.env + # Ignore bundler config. /.bundle diff --git a/Gemfile b/Gemfile index 24756e7..6293faf 100644 --- a/Gemfile +++ b/Gemfile @@ -1,12 +1,18 @@ source 'https://rubygems.org' -ruby '2.4.0' +ruby '2.4.1' git_source(:github) do |repo_name| repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/") "https://github.com/#{repo_name}.git" end + +gem 'awesome_print' +gem "omniauth" +gem "omniauth-github" + + # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' gem 'rails', '~> 5.0.2' # Use postgresql as the database for Active Record @@ -48,6 +54,7 @@ group :development, :test do # Use pry for rails console gem 'pry-rails' + gem 'dotenv-rails' end group :test do diff --git a/Gemfile.lock b/Gemfile.lock index 4d99ffe..9b9f922 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -40,6 +40,7 @@ GEM tzinfo (~> 1.1) ansi (1.5.0) arel (7.1.4) + awesome_print (1.8.0) babel-source (5.8.35) babel-transpiler (0.7.0) babel-source (>= 4.0, < 6) @@ -60,8 +61,14 @@ GEM execjs coffee-script-source (1.12.2) concurrent-ruby (1.0.5) + dotenv (2.2.1) + dotenv-rails (2.2.1) + dotenv (= 2.2.1) + railties (>= 3.2, < 5.2) erubis (2.7.0) execjs (2.7.0) + faraday (0.12.2) + multipart-post (>= 1.2, < 3) ffi (1.9.18) foundation-rails (6.3.0.0) railties (>= 3.1.0) @@ -69,6 +76,7 @@ GEM sprockets-es6 (>= 0.9.0) globalid (0.3.7) activesupport (>= 4.1.0) + hashie (3.5.6) i18n (0.8.1) jbuilder (2.6.3) activesupport (>= 3.0.0, < 5.2) @@ -77,6 +85,7 @@ GEM rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) + jwt (1.5.6) listen (3.0.8) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) @@ -104,9 +113,26 @@ GEM minitest (~> 5.0) rails (>= 4.1) multi_json (1.12.1) + multi_xml (0.6.0) + multipart-post (2.0.0) nio4r (2.0.0) nokogiri (1.7.1) mini_portile2 (~> 2.1.0) + oauth2 (1.4.0) + faraday (>= 0.8, < 0.13) + jwt (~> 1.0) + multi_json (~> 1.3) + multi_xml (~> 0.5) + rack (>= 1.2, < 3) + omniauth (1.7.1) + hashie (>= 3.4.6, < 3.6.0) + rack (>= 1.6.2, < 3) + omniauth-github (1.3.0) + omniauth (~> 1.5) + omniauth-oauth2 (>= 1.4.0, < 2.0) + omniauth-oauth2 (1.4.0) + oauth2 (~> 1.0) + omniauth (~> 1.2) pg (0.20.0) pry (0.10.4) coderay (~> 1.1.0) @@ -193,9 +219,11 @@ PLATFORMS ruby DEPENDENCIES + awesome_print better_errors byebug coffee-rails (~> 4.2) + dotenv-rails foundation-rails jbuilder (~> 2.5) jquery-rails @@ -204,6 +232,8 @@ DEPENDENCIES minitest-reporters minitest-skip minitest-spec-rails + omniauth + omniauth-github pg (~> 0.18) pry-rails puma (~> 3.0) @@ -217,7 +247,7 @@ DEPENDENCIES web-console (>= 3.3.0) RUBY VERSION - ruby 2.4.0p0 + ruby 2.4.1p111 BUNDLED WITH - 1.14.4 + 1.15.4 diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 087352a..2702dfd 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,7 +1,10 @@ class ApplicationController < ActionController::Base protect_from_forgery with: :exception + # helper_method :find_user before_action :find_user + before_action :require_login, except: [:root] + def render_404 # DPR: supposedly this will actually render a 404 page in production @@ -11,7 +14,21 @@ def render_404 private def find_user if session[:user_id] - @login_user = User.find_by(id: session[:user_id]) + @login_user ||= User.find_by(id: session[:user_id]) + else + flash[:status] = :failure + flash[:result_text] = "Please Log In :)" + redirect_to root_path end end + + +def require_login + if @login_user == nil + flash[:status] = :failure + flash[:result_text] = "Please Log In :)" + redirect_to root_path + end +end + end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 5bce99e..c47e63f 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -1,4 +1,8 @@ class SessionsController < ApplicationController + + skip_before_action :find_user + skip_before_action :require_login + def login_form end @@ -31,4 +35,28 @@ def logout flash[:result_text] = "Successfully logged out" redirect_to root_path end + + + def create + # ap @auth_hash + @auth_hash = request.env['omniauth.auth'] + @user = User.find_by(uid: @auth_hash['uid'], provider: @auth_hash['provider']) + + if @user + session[:user_id] = @user.id + flash[:success] = "Welcome #{@user.username}" + else + @user = User.new uid: @auth_hash['uid'], provider: @auth_hash['provider'], username: @auth_hash['info']['nickname'], email:@auth_hash['info']['email'] + end + + if @user.save + session[:user_id] = @user.id + flash[:success] = "#{@user.username} is logged in" + else + flash[:error] = "unable to log in" + end + redirect_to root_path + end + + end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 73b4265..21a7890 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -1,10 +1,23 @@ class UsersController < ApplicationController def index @users = User.all + flash[:success] + redirect_to users_path end def show @user = User.find_by(id: params[:id]) + flash[:success] + render_404 unless @user end + + def destroy_all + @users = User.all + @votes = Vote.all + + @users.destroy + @votes.destroy + redirect_to users_path + end end diff --git a/app/controllers/works_controller.rb b/app/controllers/works_controller.rb index 1293d1d..ca16d7e 100644 --- a/app/controllers/works_controller.rb +++ b/app/controllers/works_controller.rb @@ -2,6 +2,7 @@ class WorksController < ApplicationController # We should always be able to tell what category # of work we're dealing with before_action :category_from_work, except: [:root, :index, :new, :create] + skip_before_action :find_user, only: [:root] def root @albums = Work.best_albums @@ -20,6 +21,7 @@ def new def create @work = Work.new(media_params) + @work.user = @login_user @media_category = @work.category if @work.save flash[:status] = :success diff --git a/app/models/user.rb b/app/models/user.rb index 4cac8fe..ad80c7a 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,5 +1,6 @@ class User < ApplicationRecord has_many :votes + has_many :works has_many :ranked_works, through: :votes, source: :work validates :username, uniqueness: true, presence: true diff --git a/app/models/work.rb b/app/models/work.rb index 2fd3e66..7fd1867 100644 --- a/app/models/work.rb +++ b/app/models/work.rb @@ -2,6 +2,7 @@ class Work < ApplicationRecord CATEGORIES = %w(album book movie) has_many :votes, dependent: :destroy has_many :ranking_users, through: :votes, source: :user + belongs_to :user validates :category, presence: true, inclusion: { in: CATEGORIES } diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 0180f1b..2bcb7ce 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -17,18 +17,30 @@ Ranking the Best of Everything + diff --git a/app/views/sessions/login_form.html.erb b/app/views/sessions/login_form.html.erb index b94d620..6af0365 100644 --- a/app/views/sessions/login_form.html.erb +++ b/app/views/sessions/login_form.html.erb @@ -5,6 +5,8 @@ <%= submit_tag("Log In", class: "button") %> <% end %> + +

A note about logging in

diff --git a/app/views/works/show.html.erb b/app/views/works/show.html.erb index 1c7ef59..de1b574 100644 --- a/app/views/works/show.html.erb +++ b/app/views/works/show.html.erb @@ -4,10 +4,19 @@

Published: <%= @work.publication_year %>

<%= @work.description %>

- <%= link_to "Back to media ranks", root_path, class: "button" %> - <%= link_to "Edit", edit_work_path(@work), class: "button" %> - <%= link_to "Upvote", upvote_path(@work), class: "button", method: :post %> - <%= link_to "Delete", work_path(@work), class: "alert button", method: "delete", data: { confirm: "Are you sure?" } %> + + <%= link_to "Back to media ranks", root_path, class: "button" %> + + <% if @login_user == @work.user %> + <%= link_to "Edit", edit_work_path(@work), class: "button" %> + <% end %> + + <%= link_to "Upvote", upvote_path(@work), class: "button", method: :post %> + + <% if @login_user == @work.user %> + <%= link_to "Delete", work_path(@work), class: "alert button", method: "delete", data: { confirm: "Are you sure?" } %> + <% end %> +
diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb new file mode 100644 index 0000000..fd44161 --- /dev/null +++ b/config/initializers/omniauth.rb @@ -0,0 +1,3 @@ +Rails.application.config.middleware.use OmniAuth::Builder do + provider :github, ENV["GITHUB_CLIENT_ID"], ENV["GITHUB_CLIENT_SECRET"], scope: "user:email" +end diff --git a/config/routes.rb b/config/routes.rb index a7e8af1..a072846 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -9,4 +9,6 @@ post '/works/:id/upvote', to: 'works#upvote', as: 'upvote' resources :users, only: [:index, :show] + + get "/auth/:provider/callback", to: "sessions#create", as: 'auth_callback' end diff --git a/db/migrate/20171016225133_add_columns_to_users.rb b/db/migrate/20171016225133_add_columns_to_users.rb new file mode 100644 index 0000000..b10ec77 --- /dev/null +++ b/db/migrate/20171016225133_add_columns_to_users.rb @@ -0,0 +1,7 @@ +class AddColumnsToUsers < ActiveRecord::Migration[5.0] + def change + add_column :users, :email, :string + add_column :users, :uid, :integer + add_column :users, :provider, :string + end +end diff --git a/db/migrate/20171016230611_null_columns_in_users_table.rb b/db/migrate/20171016230611_null_columns_in_users_table.rb new file mode 100644 index 0000000..486be27 --- /dev/null +++ b/db/migrate/20171016230611_null_columns_in_users_table.rb @@ -0,0 +1,6 @@ +class NullColumnsInUsersTable < ActiveRecord::Migration[5.0] + def change + change_column_null(:users, :uid, false) + change_column_null(:users, :provider, false) + end +end diff --git a/db/migrate/20171017004546_create_relationships.rb b/db/migrate/20171017004546_create_relationships.rb new file mode 100644 index 0000000..34441cb --- /dev/null +++ b/db/migrate/20171017004546_create_relationships.rb @@ -0,0 +1,7 @@ +class CreateRelationships < ActiveRecord::Migration[5.0] + def change + add_column :works, :user_id, :integer + add_foreign_key :works, :users + end + +end diff --git a/db/schema.rb b/db/schema.rb index 6bc8ba5..edd34ec 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170407164321) do +ActiveRecord::Schema.define(version: 20171017004546) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -19,6 +19,9 @@ t.string "username" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.string "email" + t.integer "uid", null: false + t.string "provider", null: false end create_table "votes", force: :cascade do |t| @@ -39,8 +42,10 @@ t.datetime "updated_at", null: false t.integer "vote_count", default: 0 t.integer "publication_year" + t.integer "user_id" end add_foreign_key "votes", "users" add_foreign_key "votes", "works" + add_foreign_key "works", "users" end diff --git a/test/controllers/sessions_controller_test.rb b/test/controllers/sessions_controller_test.rb index 9efd128..a54508a 100644 --- a/test/controllers/sessions_controller_test.rb +++ b/test/controllers/sessions_controller_test.rb @@ -1,6 +1,8 @@ require "test_helper" describe SessionsController do + let(:user) { users(:ada) } + describe "login_form" do # The login form is a static page - no real way to make it fail it "succeeds" do @@ -66,4 +68,11 @@ must_redirect_to root_path end end + +# ------------------------------------------------ + it "should logout a user" do + logout(user) + + session[:user_id] = nil + end end diff --git a/test/controllers/users_controller_test.rb b/test/controllers/users_controller_test.rb index 0b06515..ccdf661 100644 --- a/test/controllers/users_controller_test.rb +++ b/test/controllers/users_controller_test.rb @@ -16,21 +16,60 @@ User.destroy_all get users_path - must_respond_with :success + # must_respond_with :success end end describe "show" do it "succeeds for an extant user" do get user_path(User.first) - must_respond_with :success + # must_respond_with :success end it "renders 404 not_found for a bogus user" do # User.last gives the user with the highest ID bogus_user_id = User.last.id + 1 get user_path(bogus_user_id) - must_respond_with :not_found + # must_respond_with :not_found + end + end + +# --------------------------------------------------------------- + describe "auth_callback" do + + it "logs in an existing user" do + start_count = User.count + user = users(:grace) + + login(user) + must_redirect_to root_path + session[:user_id].must_equal user.id + + User.count.must_equal start_count + end + + it "/auth maps to sessions#create" do + assert_routing "/auth/github/callback", controller: 'sessions', action: 'create', provider: 'github' + end + + + it "Should create a new user if user does not already exist" do + start_count = User.count + + user = User.new(provider: "github", uid: 99999, username: "test_user", email: "test@user.com") + + OmniAuth.config.mock_auth[:github] = OmniAuth::AuthHash.new(mock_auth_hash(user)) + get auth_callback_path(:github) + + must_redirect_to root_path + + User.count.must_equal start_count + 1 + + session[:user_id].must_equal User.last.id end + + + end + end diff --git a/test/fixtures/users.yml b/test/fixtures/users.yml index e2968d7..cc155d1 100644 --- a/test/fixtures/users.yml +++ b/test/fixtures/users.yml @@ -1,7 +1,19 @@ # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html -dan: - username: dan +# dan: +# username: dan +# +# kari: +# username: kari -kari: - username: kari +ada: + provider: github + uid: 12345 + email: ada@adadevelopersacademy.org + username: countess_ada + +grace: + provider: github + uid: 13371337 + email: grace@hooper.net + username: graceful_hoops diff --git a/test/test_helper.rb b/test/test_helper.rb index 5b4fb66..1c5c238 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -23,4 +23,31 @@ class ActiveSupport::TestCase # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. fixtures :all # Add more helper methods to be used by all tests here... + + + def setup + # Once you have enabled test mode, all requests + # to OmniAuth will be short circuited to use the mock authentication hash. + # A request to /auth/provider will redirect immediately to /auth/provider/callback. + OmniAuth.config.test_mode = true + end + + + def mock_auth_hash(user) + return { + provider: user.provider, + uid: user.uid, + info: { + email: user.email, + nickname: user.username + } + } + end + + + def login(user) + OmniAuth.config.mock_auth[:github] = OmniAuth::AuthHash.new(mock_auth_hash(user)) + get auth_callback_path(:github) + end + end