From 42433f1c640eb9a368e783d8c52e20f9a9de92af Mon Sep 17 00:00:00 2001 From: Richard Ortega Date: Mon, 2 Jul 2012 06:43:41 -0500 Subject: [PATCH] Add user following --- app/controllers/relationships_controller.rb | 21 ++++ app/models/micropost.rb | 7 ++ app/models/user.rb | 4 +- app/views/relationships/create.js.erb | 2 + app/views/relationships/destroy.js.erb | 2 + app/views/users/_follow.html.erb | 2 +- app/views/users/_unfollow.html.erb | 2 +- .../relationships_controller_spec.rb | 41 ++++++ spec/models/user_spec.rb | 11 ++ spec/requests/authentication_pages_spec.rb | 119 +++++++++++------- 10 files changed, 160 insertions(+), 51 deletions(-) create mode 100644 app/controllers/relationships_controller.rb create mode 100644 app/views/relationships/create.js.erb create mode 100644 app/views/relationships/destroy.js.erb create mode 100644 spec/controllers/relationships_controller_spec.rb diff --git a/app/controllers/relationships_controller.rb b/app/controllers/relationships_controller.rb new file mode 100644 index 0000000..1a564d4 --- /dev/null +++ b/app/controllers/relationships_controller.rb @@ -0,0 +1,21 @@ +class RelationshipsController < ApplicationController + before_filter :signed_in_user + + def create + @user = User.find(params[:relationship][:followed_id]) + current_user.follow!(@user) + respond_to do |format| + format.html { redirect_to @user } + format.js + end + end + + def destroy + @user = Relationship.find(params[:id]).followed + current_user.unfollow!(@user) + respond_to do |format| + format.html { redirect_to @user } + format.js + end + end +end \ No newline at end of file diff --git a/app/models/micropost.rb b/app/models/micropost.rb index bc844a1..1fb9b52 100644 --- a/app/models/micropost.rb +++ b/app/models/micropost.rb @@ -8,4 +8,11 @@ class Micropost < ActiveRecord::Base # order reverse chronologically with microposts default_scope order: 'microposts.created_at DESC' + + # Returns microposts from the users being followed by the given user. + def self.from_users_followed_by(user) + followed_user_ids = "SELECT followed_id FROM relationships + WHERE follower_id = :user_id" + where("user_id IN (#{followed_user_ids}) OR user_id = :user_id", user_id: user.id) + end end diff --git a/app/models/user.rb b/app/models/user.rb index d3024c6..0908095 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -35,7 +35,9 @@ class User < ActiveRecord::Base def feed # This is preliminary. See "Following users" for the full implementation. - Micropost.where("user_id = ?", id) + #Micropost.where("user_id = ?", id) + # Full implementation + Micropost.from_users_followed_by(self) end def following?(other_user) diff --git a/app/views/relationships/create.js.erb b/app/views/relationships/create.js.erb new file mode 100644 index 0000000..6adae73 --- /dev/null +++ b/app/views/relationships/create.js.erb @@ -0,0 +1,2 @@ +$("#follow_form").html("<%= escape_javascript(render('users/unfollow')) %>") +$("#followers").html('<%= @user.followers.count %>') \ No newline at end of file diff --git a/app/views/relationships/destroy.js.erb b/app/views/relationships/destroy.js.erb new file mode 100644 index 0000000..6382b4a --- /dev/null +++ b/app/views/relationships/destroy.js.erb @@ -0,0 +1,2 @@ +$("#follow_form").html("<%= escape_javascript(render('users/follow')) %>") +$("#followers").html('<%= @user.followers.count %>') \ No newline at end of file diff --git a/app/views/users/_follow.html.erb b/app/views/users/_follow.html.erb index bc0c7b3..0b87064 100644 --- a/app/views/users/_follow.html.erb +++ b/app/views/users/_follow.html.erb @@ -1,4 +1,4 @@ -<%= form_for(current_user.relationships.build(followed_id: @user.id)) do |f| %> +<%= form_for(current_user.relationships.build(followed_id: @user.id), remote: true) do |f| %>
<%= f.hidden_field :followed_id %>
<%= f.submit "Follow", class: "btn btn-large btn-primary" %> <% end %> \ No newline at end of file diff --git a/app/views/users/_unfollow.html.erb b/app/views/users/_unfollow.html.erb index 482a863..7d114dc 100644 --- a/app/views/users/_unfollow.html.erb +++ b/app/views/users/_unfollow.html.erb @@ -1,3 +1,3 @@ -<%= form_for(current_user.relationships.find_by_followed_id(@user), html: { method: :delete}) do |f| %> +<%= form_for(current_user.relationships.find_by_followed_id(@user), html: { method: :delete}, remote: true) do |f| %> <%= f.submit "Unfollow", class: "btn btn-large" %> <% end %> \ No newline at end of file diff --git a/spec/controllers/relationships_controller_spec.rb b/spec/controllers/relationships_controller_spec.rb new file mode 100644 index 0000000..e09a3ed --- /dev/null +++ b/spec/controllers/relationships_controller_spec.rb @@ -0,0 +1,41 @@ +require 'spec_helper' + +describe RelationshipsController do + + let(:user) { FactoryGirl.create(:user) } + let(:other_user) { FactoryGirl.create(:user) } + + before { sign_in user } + + describe "creating a relationship with Ajax" do + + it "should increment the Relationship count" do + expect do + xhr :post, :create, relationship: { followed_id: other_user.id } + end.should change(Relationship, :count).by(1) + end + + it "should respond with success" do + xhr :post, :create, relationship: { followed_id: other_user.id } + response.should be_success + end + end + + describe "destroying a relationship with Ajax" do + + before { user.follow!(other_user) } + let(:relationship) { user.relationships.find_by_followed_id(other_user) } + + it "should decrement the Relationship count" do + expect do + xhr :delete, :destroy, id: relationship.id + end.should change(Relationship, :count).by(-1) + end + + it "should respond with success" do + xhr :delete, :destroy, id: relationship.id + response.should be_success + end + end + +end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index eb760eb..4619ab5 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -152,10 +152,21 @@ let(:unfollowed_post) do FactoryGirl.create(:micropost, user: FactoryGirl.create(:user)) end + let(:followed_user) { FactoryGirl.create(:user) } + + before do + @user.follow!(followed_user) + 3.times { followed_user.microposts.create!(content: "Lorem ipsum") } + end its(:feed) { should include(newer_micropost) } its(:feed) { should include(older_micropost) } its(:feed) { should_not include(unfollowed_post) } + its(:feed) do + followed_user.microposts.each do |micropost| + should include(micropost) + end + end end end diff --git a/spec/requests/authentication_pages_spec.rb b/spec/requests/authentication_pages_spec.rb index 794578e..d980f28 100644 --- a/spec/requests/authentication_pages_spec.rb +++ b/spec/requests/authentication_pages_spec.rb @@ -1,55 +1,56 @@ require 'spec_helper' describe "Authentication" do - + subject { page } describe "signin page" do - before { visit signin_path } + before { visit signin_path } - it { should have_selector('h1', text: 'Sign in') } - it { should have_selector('title', text: 'Sign in') } + it { should have_selector('h1', text: 'Sign in') } + it { should have_selector('title', text: 'Sign in') } end describe "signin" do - before { visit signin_path } + before { visit signin_path } - describe "with invalid information" do - before { click_button "Sign in" } + describe "with invalid information" do + before { click_button "Sign in" } - it { should have_selector('title', text: 'Sign in') } - it { should have_selector('div.alert.alert-error', text: 'Invalid') } + it { should have_selector('title', text: 'Sign in') } + it { should have_selector('div.alert.alert-error', text: 'Invalid') } - describe "after visiting another page" do - before { click_link "Home" } - it { should_not have_selector('div.alert.alert-error') } - end - end + describe "after visiting another page" do + before { click_link "Home" } + it { should_not have_selector('div.alert.alert-error') } + end + end + + describe "with valid information" do + let(:user) { FactoryGirl.create(:user) } + before do + fill_in "Email", with: user.email + fill_in "Password", with: user.password + click_button "Sign in" + end - describe "with valid information" do - let(:user) { FactoryGirl.create(:user) } - before { sign_in user } + it { should have_selector('title', text: user.name) } - it { should have_selector('title', text: user.name) } - - it { should have_link('Users', href: users_path)} - it { should have_link('Profile', href: user_path(user)) } - it { should have_link('Settings', href: edit_user_path(user)) } - it { should have_link('Sign out', href: signout_path) } - - it { should_not have_link('Sign in', href: signin_path) } + it { should have_link('Users', href: users_path) } + it { should have_link('Profile', href: user_path(user)) } + it { should have_link('Settings', href: edit_user_path(user)) } + it { should have_link('Sign out', href: signout_path) } + it { should_not have_link('Sign in', href: signin_path) } describe "followed by signout" do before { click_link "Sign out" } it { should have_link('Sign in') } end - end + end end describe "authorization" do - - describe "for non-signed-in users" do let(:user) { FactoryGirl.create(:user) } @@ -66,23 +67,23 @@ it "should render the desired protected page" do page.should have_selector('title', text: 'Edit user') end - end - describe "in the Microposts controller" do - - describe "submitting to the create action" do - before { post microposts_path } - specify { response.should redirect_to(signin_path) } - end - - describe "submitting to the destroy action" do - before { delete micropost_path(FactoryGirl.create(:micropost)) } - specify { response.should redirect_to(signin_path)} + describe "when signing in again" do + before do + delete signout_path + visit signin_path + fill_in "Email", with: user.email + fill_in "Password", with: user.password + click_button "Sign in" + end + + it "should render the default (profile) page" do + page.should have_selector('title', text: user.name) + end + end end end - end - describe "in the Users controller" do describe "visiting the edit page" do @@ -95,7 +96,7 @@ specify { response.should redirect_to(signin_path) } end - describe "visiting the user index" do + describe "visiting user index" do before { visit users_path } it { should have_selector('title', text: 'Sign in') } end @@ -109,7 +110,31 @@ before { visit followers_user_path(user) } it { should have_selector('title', text: 'Sign in') } end + end + + describe "in the Microposts controller" do + + describe "submitting to the create action" do + before { post microposts_path } + specify { response.should redirect_to(signin_path) } + end + describe "submitting to the destroy action" do + before { delete micropost_path(FactoryGirl.create(:micropost)) } + specify { response.should redirect_to(signin_path) } + end + end + + describe "in the Relationships controller" do + describe "submitting to the create action" do + before { post relationships_path } + specify { response.should redirect_to(signin_path) } + end + + describe "submitting to the destroy action" do + before { delete relationship_path(1) } + specify { response.should redirect_to(signin_path) } + end end end @@ -120,7 +145,7 @@ describe "visiting Users#edit page" do before { visit edit_user_path(wrong_user) } - it { should_not have_selector('title', text: full_title('Edit user')) } + it { should have_selector('title', text: full_title('')) } end describe "submitting a PUT request to the Users#update action" do @@ -135,12 +160,10 @@ before { sign_in non_admin } - describe "submitting a DELETE request to the Users#Destroy action" do + describe "submitting a DELETE request to the Users#destroy action" do before { delete user_path(user) } - specify { response.should redirect_to(root_path) } + specify { response.should redirect_to(root_path) } end end - end - -end +end \ No newline at end of file