Skip to content

Commit d44cc82

Browse files
committed
Through following HTML
1 parent 9c9bdcf commit d44cc82

20 files changed

+318
-28
lines changed

app/assets/stylesheets/custom.css.scss

+29
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,35 @@ aside {
147147
margin-right: 10px;
148148
}
149149

150+
.stats {
151+
overflow: auto;
152+
a {
153+
float: left;
154+
padding: 0 10px;
155+
border-left: 1px solid $grayLighter;
156+
color: gray;
157+
&:first-child {
158+
padding-left: 0;
159+
border: 0;
160+
}
161+
&:hover {
162+
text-decoration: none;
163+
color: $blue;
164+
}
165+
}
166+
strong {
167+
display: block;
168+
}
169+
}
170+
171+
.user_avatars {
172+
overflow: auto;
173+
margin-top: 10px;
174+
.gravatar {
175+
margin: 1px 1px;
176+
}
177+
}
178+
150179
/* forms */
151180

152181
input, textarea, select, .uneditable-input {

app/controllers/users_controller.rb

+16-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
class UsersController < ApplicationController
2-
before_action :signed_in_user, only: [:index, :edit, :update, :destroy]
2+
before_action :signed_in_user,
3+
only: [:index, :edit, :update, :destroy, :following, :followers]
34
before_action :correct_user, only: [:edit, :update]
45
before_action :admin_user, only: :destroy
56

@@ -46,6 +47,20 @@ def destroy
4647
redirect_to users_url
4748
end
4849

50+
def following
51+
@title = "Following"
52+
@user = User.find(params[:id])
53+
@users = @user.followed_users.paginate(page: params[:page])
54+
render 'show_follow'
55+
end
56+
57+
def followers
58+
@title = "Followers"
59+
@user = User.find(params[:id])
60+
@users = @user.followers.paginate(page: params[:page])
61+
render 'show_follow'
62+
end
63+
4964
private
5065

5166
def user_params

app/models/relationship.rb

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
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

app/models/user.rb

+18
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
class User < ActiveRecord::Base
22
has_many :microposts, dependent: :destroy
3+
has_many :relationships, foreign_key: "follower_id", dependent: :destroy
4+
has_many :followed_users, through: :relationships, source: :followed
5+
has_many :reverse_relationships, foreign_key: "followed_id",
6+
class_name: "Relationship",
7+
dependent: :destroy
8+
has_many :followers, through: :reverse_relationships, source: :follower
39
before_save { self.email = email.downcase }
410
before_save :create_remember_token
511
validates :name, presence: true, length: { maximum: 50 }
@@ -14,6 +20,18 @@ def feed
1420
Micropost.where("user_id = ?", id)
1521
end
1622

23+
def following?(other_user)
24+
relationships.find_by(followed_id: other_user.id)
25+
end
26+
27+
def follow!(other_user)
28+
relationships.create!(followed_id: other_user.id)
29+
end
30+
31+
def unfollow!(other_user)
32+
relationships.find_by(followed_id: other_user.id).destroy
33+
end
34+
1735
private
1836

1937
def create_remember_token

app/views/shared/_stats.html.erb

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<% @user ||= current_user %>
2+
<div class="stats">
3+
<a href="<%= following_user_path(@user) %>">
4+
<strong id="following" class="stat">
5+
<%= @user.followed_users.count %>
6+
</strong>
7+
following
8+
</a>
9+
<a href="<%= followers_user_path(@user) %>">
10+
<strong id="followers" class="stat">
11+
<%= @user.followers.count %>
12+
</strong>
13+
followers
14+
</a>
15+
</div>

app/views/static_pages/home.html.erb

+3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
<section>
55
<%= render 'shared/user_info' %>
66
</section>
7+
<section>
8+
<%= render 'shared/stats' %>
9+
</section>
710
<section>
811
<%= render 'shared/micropost_form' %>
912
</section>

app/views/users/_follow.html.erb

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<%= form_for(current_user.relationships.build(followed_id: @user.id)) do |f| %>
2+
<div><%= f.hidden_field :followed_id %></div>
3+
<%= f.submit "Follow", class: "btn btn-large btn-primary" %>
4+
<% end %>

app/views/users/_follow_form.html.erb

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<% unless current_user?(@user) %>
2+
<div id="follow_form">
3+
<% if current_user.following?(@user) %>
4+
<%= render 'unfollow' %>
5+
<% else %>
6+
<%= render 'follow' %>
7+
<% end %>
8+
</div>
9+
<% end %>

app/views/users/_unfollow.html.erb

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<%= form_for(current_user.relationships.find_by(followed_id: @user),
2+
html: { method: :delete }) do |f| %>
3+
<%= f.submit "Unfollow", class: "btn btn-large" %>
4+
<% end %>

app/views/users/show.html.erb

+4
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,12 @@
77
<%= @user.name %>
88
</h1>
99
</section>
10+
<section>
11+
<%= render 'shared/stats' %>
12+
</section>
1013
</aside>
1114
<div class="span8">
15+
<%= render 'follow_form' if signed_in? %>
1216
<% if @user.microposts.any? %>
1317
<h3>Microposts (<%= @user.microposts.count %>)</h3>
1418
<ol class="microposts">

app/views/users/show_follow.html.erb

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<% provide(:title, @title) %>
2+
<div class="row">
3+
<aside class="span4">
4+
<section>
5+
<%= gravatar_for @user %>
6+
<h1><%= @user.name %></h1>
7+
<span><%= link_to "view my profile", @user %></span>
8+
<span><b>Microposts:</b> <%= @user.microposts.count %></span>
9+
</section>
10+
<section>
11+
<%= render 'shared/stats' %>
12+
<% if @users.any? %>
13+
<div class="user_avatars">
14+
<% @users.each do |user| %>
15+
<%= link_to gravatar_for(user, size: 30), user %>
16+
<% end %>
17+
</div>
18+
<% end %>
19+
</section>
20+
</aside>
21+
<div class="span8">
22+
<h3><%= @title %></h3>
23+
<% if @users.any? %>
24+
<ul class="users">
25+
<%= render @users %>
26+
</ul>
27+
<%= will_paginate %>
28+
<% end %>
29+
</div>
30+
</div>

config/routes.rb

+8-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
SampleApp::Application.routes.draw do
2-
resources :users
3-
resources :sessions, only: [:new, :create, :destroy]
4-
resources :microposts, only: [:create, :destroy]
2+
resources :users do
3+
member do
4+
get :following, :followers
5+
end
6+
end
7+
resources :sessions, only: [:new, :create, :destroy]
8+
resources :microposts, only: [:create, :destroy]
9+
resources :relationships, only: [:create, :destroy]
510
root to: 'static_pages#home'
611
match '/signup', to: 'users#new', via: 'get'
712
match '/signin', to: 'sessions#new', via: 'get'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
class CreateRelationships < ActiveRecord::Migration
2+
def change
3+
create_table :relationships do |t|
4+
t.integer :follower_id
5+
t.integer :followed_id
6+
7+
t.timestamps
8+
end
9+
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

db/schema.rb

+12-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
#
1212
# It's strongly recommended that you check this file into your version control system.
1313

14-
ActiveRecord::Schema.define(version: 20130315175534) do
14+
ActiveRecord::Schema.define(version: 20130315230445) do
1515

1616
create_table "microposts", force: true do |t|
1717
t.string "content"
@@ -22,6 +22,17 @@
2222

2323
add_index "microposts", ["user_id", "created_at"], name: "index_microposts_on_user_id_and_created_at"
2424

25+
create_table "relationships", force: true do |t|
26+
t.integer "follower_id"
27+
t.integer "followed_id"
28+
t.datetime "created_at"
29+
t.datetime "updated_at"
30+
end
31+
32+
add_index "relationships", ["followed_id"], name: "index_relationships_on_followed_id"
33+
add_index "relationships", ["follower_id", "followed_id"], name: "index_relationships_on_follower_id_and_followed_id", unique: true
34+
add_index "relationships", ["follower_id"], name: "index_relationships_on_follower_id"
35+
2536
create_table "users", force: true do |t|
2637
t.string "name"
2738
t.string "email"

lib/tasks/sample_data.rake

+36-19
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,42 @@
11
namespace :db do
22
desc "Fill database with sample data"
33
task populate: :environment do
4-
admin = User.create!(name: "Example User",
5-
6-
password: "foobar",
7-
password_confirmation: "foobar",
8-
admin: true)
9-
99.times do |n|
10-
name = Faker::Name.name
11-
email = "example-#{n+1}@railstutorial.org"
12-
password = "password"
13-
User.create!(name: name,
14-
email: email,
15-
password: password,
16-
password_confirmation: password)
17-
end
4+
make_users
5+
make_microposts
6+
make_relationships
7+
end
8+
end
9+
10+
def make_users
11+
admin = User.create!(name: "Example User",
12+
13+
password: "foobar",
14+
password_confirmation: "foobar",
15+
admin: true)
16+
99.times do |n|
17+
name = Faker::Name.name
18+
email = "example-#{n+1}@railstutorial.org"
19+
password = "password"
20+
User.create!(name: name,
21+
email: email,
22+
password: password,
23+
password_confirmation: password)
24+
end
25+
end
1826

19-
users = User.all(limit: 6)
20-
50.times do
21-
content = Faker::Lorem.sentence(5)
22-
users.each { |user| user.microposts.create!(content: content) }
23-
end
27+
def make_microposts
28+
users = User.all(limit: 6)
29+
50.times do
30+
content = Faker::Lorem.sentence(5)
31+
users.each { |user| user.microposts.create!(content: content) }
2432
end
33+
end
34+
35+
def make_relationships
36+
users = User.all
37+
user = users.first
38+
followed_users = users[2..50]
39+
followers = users[3..40]
40+
followed_users.each { |followed| user.follow!(followed) }
41+
followers.each { |follower| follower.follow!(user) }
2542
end

spec/models/relationship_spec.rb

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
require 'spec_helper'
2+
3+
describe Relationship do
4+
5+
let(:follower) { FactoryGirl.create(:user) }
6+
let(:followed) { FactoryGirl.create(:user) }
7+
let(:relationship) { follower.relationships.build(followed_id: followed.id) }
8+
9+
subject { relationship }
10+
11+
it { should be_valid }
12+
13+
describe "follower methods" do
14+
it { should respond_to(:follower) }
15+
it { should respond_to(:followed) }
16+
its(:follower) { should eql(follower) }
17+
its(:followed) { should eql(followed) }
18+
end
19+
20+
describe "when followed id is not present" do
21+
before { relationship.followed_id = nil }
22+
it { should_not be_valid }
23+
end
24+
25+
describe "when follower id is not present" do
26+
before { relationship.follower_id = nil }
27+
it { should_not be_valid }
28+
end
29+
end

spec/models/user_spec.rb

+30-1
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,18 @@
1414
it { should respond_to(:password_digest) }
1515
it { should respond_to(:password) }
1616
it { should respond_to(:password_confirmation) }
17-
it { should respond_to(:password_confirmation) }
1817
it { should respond_to(:remember_token) }
1918
it { should respond_to(:authenticate) }
2019
it { should respond_to(:admin) }
2120
it { should respond_to(:microposts) }
2221
it { should respond_to(:feed) }
22+
it { should respond_to(:relationships) }
23+
it { should respond_to(:followed_users) }
24+
it { should respond_to(:reverse_relationships) }
25+
it { should respond_to(:followers) }
26+
it { should respond_to(:following?) }
27+
it { should respond_to(:follow!) }
28+
it { should respond_to(:unfollow!) }
2329

2430
it { should be_valid }
2531
it { should_not be_admin }
@@ -127,4 +133,27 @@
127133
its(:feed) { should_not include(unfollowed_post) }
128134
end
129135
end
136+
137+
describe "following" do
138+
let(:other_user) { FactoryGirl.create(:user) }
139+
before do
140+
@user.save
141+
@user.follow!(other_user)
142+
end
143+
144+
it { should be_following(other_user) }
145+
its(:followed_users) { should include(other_user) }
146+
147+
describe "followed user" do
148+
subject { other_user }
149+
its(:followers) { should include(@user) }
150+
end
151+
152+
describe "and unfollowing" do
153+
before { @user.unfollow!(other_user) }
154+
155+
it { should_not be_following(other_user) }
156+
its(:followed_users) { should_not include(other_user) }
157+
end
158+
end
130159
end

0 commit comments

Comments
 (0)