Commit 3a70729

mo khan <mo@mokhan.ca>
2016-05-01 18:10:15
use user_session.id instead of user.id
1 parent 29a69b0
app/controllers/application_controller.rb
@@ -8,12 +8,8 @@ class ApplicationController < ActionController::Base
 
   protected
 
-  def log_in(user)
-    session[:user_id] = user.id
-  end
-
-  def current_user
-    @current_user ||= User.find(session[:user_id])
+  def current_user(session_id = session[:user_id])
+    @current_user ||= UserSession.authenticate(session_id).try(:user)
   end
 
   def translate(key)
@@ -21,7 +17,7 @@ class ApplicationController < ActionController::Base
   end
 
   def authenticate!
-    return if session[:user_id].present? && current_user.present?
+    return if current_user.present?
     redirect_to new_session_path
   rescue
     redirect_to new_session_path
app/controllers/profiles_controller.rb
@@ -6,12 +6,12 @@ class ProfilesController < ApplicationController
   end
 
   def edit
-    @profile = @current_user.profile
+    @profile = current_user.profile
     @program = Program.stronglifts
   end
 
   def update
-    profile = @current_user.profile
+    profile = current_user.profile
     profile.update_attributes(profile_params)
     flash[:notice] = t("profiles.edit.profile_update_success")
     redirect_to profile_path(profile)
app/controllers/registrations_controller.rb
@@ -6,7 +6,9 @@ class RegistrationsController < PublicController
   def create
     @user = User.new(secure_params)
     if @user.save
-      log_in(@user)
+      session[:user_id] = User.
+        login(secure_params[:username], secure_params[:password]).
+        access(request)
       UserMailer.registration_email(@user).deliver_later
       flash[:notice] = translate(".success")
       redirect_to dashboard_path
app/controllers/sessions_controller.rb
@@ -1,7 +1,7 @@
 class SessionsController < PublicController
   def create
-    if user_session = UserSession.login(params[:user][:username], params[:user][:password])
-      session[:user_id] = user_session.id
+    if user_session = User.login(params[:user][:username], params[:user][:password])
+      session[:user_id] = user_session.access(request)
       redirect_to dashboard_path
     else
       flash[:warning] = t("sessions.create.invalid_login")
@@ -14,6 +14,7 @@ class SessionsController < PublicController
   end
 
   def destroy
+    UserSession.authenticate(session[:user_id]).try(:revoke!)
     reset_session()
     redirect_to root_path
   end
app/models/gym.rb
@@ -1,13 +1,7 @@
 class Gym < ActiveRecord::Base
   validates_presence_of :name
   has_one :location, as: :locatable
-  before_save :assign_location
+  accepts_nested_attributes_for :location
 
   scope :closest_to, ->(user) { all }
-
-  private
-
-  def assign_location
-    #self.latitude, self.longitude = Location.from(address, city, region, country)
-  end
 end
app/models/location.rb
@@ -1,4 +1,6 @@
 class Location < ActiveRecord::Base
+  before_save :assign_coordinates
+
   def self.build_from_ip(ip)
   end
 
@@ -6,4 +8,10 @@ class Location < ActiveRecord::Base
     results = Geocoder.search("#{address}, #{city}, #{region}, #{country}")
     results.any? ? results.first.coordinates : [nil, nil]
   end
+
+  private
+
+  def assign_coordinates
+    self.latitude, self.longitude = Location.from(address, city, region, country)
+  end
 end
app/models/user.rb
@@ -2,6 +2,7 @@ class User < ActiveRecord::Base
   has_secure_password
   has_many :training_sessions
   has_many :exercise_sessions, through: :training_sessions
+  has_many :user_sessions, dependent: :destroy
   has_one :profile
   USERNAME_REGEX=/\A[-a-z0-9_.]*\z/i
 
@@ -28,8 +29,7 @@ class User < ActiveRecord::Base
     exercise_sessions.
       joins(:exercise).
       where(exercises: { name: exercise.name }).
-      pluck(:target_weight).
-      max
+      maximum(:target_weight)
   end
 
   def history_for(exercise)
@@ -53,6 +53,18 @@ class User < ActiveRecord::Base
     GoogleDrive.new(self)
   end
 
+  class << self
+    def login(username, password)
+      user = User.find_by(
+        "email = :email OR username = :username",
+        username: username.downcase,
+        email: username.downcase
+      )
+      return false if user.blank?
+      user.user_sessions.create! if user.authenticate(password)
+    end
+  end
+
   private
 
   def create_profile
app/models/user_session.rb
@@ -14,23 +14,12 @@ class UserSession < ActiveRecord::Base
     self.ip = request.ip
     self.user_agent = request.user_agent
     self.location = Location.build_from_ip(request.ip)
-    id
+    save ? id : nil
   end
 
   class << self
     def authenticate(id)
       active.find_by(id: id)
     end
-
-    def login(username, password)
-      user = User.find_by(
-        "email = :email OR username = :username",
-        username: username.downcase,
-        email: username.downcase
-      )
-      if user.present?
-        user.authenticate(password)
-      end
-    end
   end
 end
db/migrate/20160430155822_create_user_sessions.rb
@@ -1,7 +1,7 @@
 class CreateUserSessions < ActiveRecord::Migration
   def change
     create_table :user_sessions, id: :uuid do |t|
-      t.belongs_to :user, foreign_key: true, type: :uuid, index: true
+      t.belongs_to :user, foreign_key: true, type: :uuid, index: true, null: false
       t.string :ip
       t.text :user_agent
       t.datetime :accessed_at
db/schema.rb
@@ -94,7 +94,7 @@ ActiveRecord::Schema.define(version: 20160430155822) do
   add_index "training_sessions", ["user_id"], name: "index_training_sessions_on_user_id", using: :btree
 
   create_table "user_sessions", id: :uuid, default: "uuid_generate_v4()", force: :cascade do |t|
-    t.uuid     "user_id"
+    t.uuid     "user_id",     null: false
     t.string   "ip"
     t.text     "user_agent"
     t.datetime "accessed_at"
spec/controllers/profiles_controller_spec.rb
@@ -15,12 +15,14 @@ describe ProfilesController do
       it "loads the user's profile" do
         get :show, id: user.to_param
         expect(assigns(:user)).to eql(user)
+        expect(assigns(:profile)).to eql(user.profile)
         expect(assigns(:program)).to eql(Program.stronglifts)
       end
 
       it "loads the other user's profile" do
         get :show, id: other_user.to_param
         expect(assigns(:user)).to eql(other_user)
+        expect(assigns(:profile)).to eql(other_user.profile)
         expect(assigns(:program)).to eql(Program.stronglifts)
       end
     end
@@ -30,13 +32,13 @@ describe ProfilesController do
 
       it "loads the user's profile into an edit view" do
         get :edit, id: user.to_param
-        expect(assigns(:current_user)).to eql(user)
+        expect(assigns(:profile)).to eql(user.profile)
         expect(assigns(:program)).to eql(Program.stronglifts)
       end
 
       it "will not load the other user's profile into an edit view" do
         get :edit, id: other_user.to_param
-        expect(assigns(:current_user)).to eql(user)
+        expect(assigns(:profile)).to eql(user.profile)
         expect(assigns(:program)).to eql(Program.stronglifts)
       end
     end
spec/controllers/registrations_controller_spec.rb
@@ -38,7 +38,8 @@ describe RegistrationsController do
       end
 
       it "logs them in" do
-        expect(session[:user_id]).to eql(User.first.id)
+        expect(session[:user_id]).to be_present
+        expect(session[:user_id]).to eql(UserSession.last.id)
       end
 
       it "does not display any errors" do
spec/controllers/sessions_controller_spec.rb
@@ -7,12 +7,12 @@ describe SessionsController do
     context "when credentials are correct" do
       it "logs you in with email" do
         post :create, { user: { username: user.email, password: "password" } }
-        expect(session[:user_id]).to eql(user.id)
+        expect(session[:user_id]).to eql(UserSession.last.id)
       end
 
       it "logs you in with username" do
         post :create, { user: { username: user.username, password: "password" } }
-        expect(session[:user_id]).to eql(user.id)
+        expect(session[:user_id]).to eql(UserSession.last.id)
       end
 
       it "redirects to the dashboard" do
@@ -37,11 +37,15 @@ describe SessionsController do
   describe "#destroy" do
     context "when logged in" do
       let(:user) { create(:user) }
+      let(:user_session) { create(:active_session, user: user) }
 
       it "logs you out" do
-        session[:user_id] = user.id
+        session[:user_id] = user_session.id
+
         delete :destroy, id: user.id
+
         expect(session[:user_id]).to be_nil
+        expect(user_session.reload.revoked_at).to be_present
       end
     end
   end
spec/features/profiles_spec.rb
@@ -3,14 +3,16 @@ require "rails_helper"
 feature "Profiles", type: :feature do
   include_context "stronglifts_program"
   let(:user) { create(:user) }
+  let(:user_session) { create(:active_session, user: user) }
+
+  before :each do
+    page.set_rack_session(user_id: user_session.id)
+  end
 
   context "when the user has not completed any workouts" do
     subject { ProfilePage.new(user) }
 
-    before :each do
-      page.set_rack_session(user_id: user.id)
-      subject.visit_page
-    end
+    before { subject.visit_page }
 
     it "displays the users username" do
       expect(page).to have_content(user.username)
@@ -25,10 +27,7 @@ feature "Profiles", type: :feature do
   context "editing my profile" do
     subject { EditProfilePage.new(user) }
 
-    before :each do
-      page.set_rack_session(user_id: user.id)
-      subject.visit_page
-    end
+    before { subject.visit_page }
 
     it "allows me to edit my profile" do
       subject.change(gender: :male, social_tolerance: :low)
spec/models/gym_spec.rb
@@ -11,23 +11,20 @@ describe Gym do
     end
   end
 
-  describe "#before_save" do
-    let(:latitude) { rand(90.0) }
-    let(:longitude) { rand(180.0) }
-
-    it 'updates the latitude/logitude' do
-      allow(Location).to receive(:from).and_return([latitude, longitude])
-      subject.assign_attributes(
+  describe "#location" do
+    it 'updates the location' do
+      subject.location_attributes = {
         address: '123 street sw',
         city: 'edmonton',
         region: 'alberta',
         country: 'canada',
-      )
+      }
       subject.save!
-      subject.reload
 
-      expect(subject.latitude).to eql(latitude)
-      expect(subject.longitude).to eql(longitude)
+      expect(subject.location.address).to eql('123 street sw')
+      expect(subject.location.city).to eql('edmonton')
+      expect(subject.location.region).to eql('alberta')
+      expect(subject.location.country).to eql('canada')
     end
   end
 end
spec/models/location_spec.rb
@@ -10,4 +10,24 @@ describe Location do
       expect(longitude).to be_within(0.1).of(-114.0927691)
     end
   end
+
+  describe "#before_save" do
+    let(:latitude) { rand(90.0) }
+    let(:longitude) { rand(180.0) }
+
+    it 'assigns a lat/long' do
+      allow(Location).to receive(:from).and_return([latitude, longitude])
+
+      location = Location.new(
+        address: '123 street sw',
+        city: 'edmonton',
+        region: 'alberta',
+        country: 'canada',
+      )
+      location.save!
+
+      expect(location.latitude).to eql(latitude)
+      expect(location.longitude).to eql(longitude)
+    end
+  end
 end
spec/models/user_session_spec.rb
@@ -25,6 +25,7 @@ describe UserSession do
     it { expect(subject.user_agent).to eql(request.user_agent) }
     it { expect(subject.location).to_not be_nil }
     it { expect(because).to eql(subject.id) }
+    it { expect(subject).to be_persisted }
   end
 
   describe ".active" do
@@ -56,26 +57,4 @@ describe UserSession do
       expect(UserSession.authenticate('blah')).to be_nil
     end
   end
-
-  describe "#login" do
-    context "when credentials are correct" do
-      it "returns true" do
-        user = create(:user, password: "password", password_confirmation: "password")
-        expect(UserSession.login(user.email.upcase, "password")).to eql(user)
-      end
-
-      it "is case in-sensitive for username" do
-        user = create(:user, username: "upcase", password: "password", password_confirmation: "password")
-        expect(UserSession.login("UPcase", "password")).to eql(user)
-      end
-    end
-
-    context "when the email is not registered" do
-      it { expect(UserSession.login("sofake@noteven.com", "password")).to be_nil }
-    end
-
-    context "when the username is not registered" do
-      it { expect(UserSession.login("sofake", "password")).to be_nil }
-    end
-  end
 end
spec/models/user_spec.rb
@@ -191,4 +191,30 @@ describe User do
       expect(user.profile.social_tolerance).to be_nil
     end
   end
+
+  describe "#login" do
+    context "when credentials are correct" do
+      it "returns true" do
+        user = create(:user, password: "password", password_confirmation: "password")
+        result = User.login(user.email.upcase, "password")
+        expect(result).to be_instance_of(UserSession)
+        expect(result.user).to eql(user)
+      end
+
+      it "is case in-sensitive for username" do
+        user = create(:user, username: "upcase", password: "password", password_confirmation: "password")
+        result = User.login("UPcase", "password")
+        expect(result).to be_instance_of(UserSession)
+        expect(result.user).to eql(user)
+      end
+    end
+
+    context "when the email is not registered" do
+      it { expect(User.login("sofake@noteven.com", "password")).to be_falsey }
+    end
+
+    context "when the username is not registered" do
+      it { expect(User.login("sofake", "password")).to be_falsey }
+    end
+  end
 end
spec/support/http_authentication.rb
@@ -1,5 +1,6 @@
 module HttpAuthentication
   def http_login(user)
-    session[:user_id] = user.id
+    allow(controller).to receive(:current_user).and_return(user)
+    session[:user_id] = build(:user_session, id: SecureRandom.uuid, user: user).id
   end
 end
spec/spec_helper.rb
@@ -11,4 +11,7 @@ RSpec.configure do |config|
   config.run_all_when_everything_filtered = true
   config.order = :random
   Kernel.srand config.seed
+  config.backtrace_exclusion_patterns = [
+    /usr\/local/
+  ]
 end