diff --git a/app/controllers/friends_controller.rb b/app/controllers/friends_controller.rb
new file mode 100644
index 0000000..11cdd02
--- /dev/null
+++ b/app/controllers/friends_controller.rb
@@ -0,0 +1,49 @@
+class FriendsController < ApplicationController
+ append_before_filter :fetch_people, :only => [ :show, :destroy ]
+
+ # GET /friends/1
+ # GET /friends/1.xml
+ def show
+ @content_title = "Friends of #{@people.title}"
+ @friends = @people.actual_friends
+ respond_to do |format|
+ format.html # show.rhtml
+ format.xml { render :xml => @friends.to_xml }
+ end
+ end
+
+ # POST /friends
+ # POST /friends.xml
+ def create
+ @friend = Friend.new(params[:friend])
+ respond_to do |format|
+ if @friend.save
+ flash[:notice] = 'Successfully added the new friend'
+ format.html { redirect_to people_path(@people.page.title_for_url) }
+ else
+ format.html { render :action => "new" }
+ end
+ end
+ end
+
+ # DELETE /friends/1
+ # DELETE /friends/1.xml
+ def destroy
+ @friend = Friend.find :first,
+ :conditions => [ 'source_id = ? AND destination_id = ?',
+ params[:d].to_i, @people.id ]
+ @friend.destroy if @friend
+ respond_to do |format|
+ flash[:notice] = 'Removed the friend'
+ format.html { redirect_to people_path(@people.page.title_for_url) }
+ end
+ end
+
+ protected
+
+ def fetch_people
+ @people = People.find_by_title(Page.title_from_url(params[:id]))
+ raise ActiveRecord::RecordNotFound.new if @people.nil?
+ @page = @people.page
+ end
+end
diff --git a/app/helpers/friends_helper.rb b/app/helpers/friends_helper.rb
new file mode 100644
index 0000000..24d631f
--- /dev/null
+++ b/app/helpers/friends_helper.rb
@@ -0,0 +1,3 @@
+module FriendsHelper
+ include PeoplesHelper
+end
diff --git a/app/helpers/peoples_helper.rb b/app/helpers/peoples_helper.rb
index 574fe22..dfcf705 100644
--- a/app/helpers/peoples_helper.rb
+++ b/app/helpers/peoples_helper.rb
@@ -12,4 +12,21 @@ module PeoplesHelper
link_to 'Edit People', edit_people_path(people.page.title_for_url),
{ :title => "Edit #{people.title}" }
end
+
+ def show_friends_link(people)
+ link_to "#{people.title}'s Friends (#{people.actual_friends.size})",
+ friend_path(people.page.title_for_url)
+ end
+
+ def add_friend_link(people)
+ link_to "#{image_tag('list-add.png')} Friend",
+ friends_path('friend[source_id]' => session[:people_id],
+ 'friend[destination_id]' => people.id), :method => :post
+ end
+
+ def remove_friend_link(people)
+ link_to "#{image_tag('list-remove.png')} Friend",
+ friend_url(:id => people.page.title_for_url, :d => session[:people_id]),
+ :method => :delete
+ end
end
diff --git a/app/models/friend.rb b/app/models/friend.rb
new file mode 100644
index 0000000..7b7788b
--- /dev/null
+++ b/app/models/friend.rb
@@ -0,0 +1,16 @@
+##
+# Represents a relationship of association between two People models.
+#
+class Friend < ActiveRecord::Base
+ belongs_to :source, :class_name => 'People', :foreign_key => :source_id
+ belongs_to :destination, :class_name => 'People',
+ :foreign_key => :destination_id
+ validates_presence_of :source_id, :destination_id
+ validates_uniqueness_of :destination_id, :scope => :source_id
+
+ validates_each :destination_id do |record, attr, value|
+ if value == record.source_id
+ record.errors.add(attr, 'cannot be the same as the source')
+ end
+ end
+end
diff --git a/app/models/people.rb b/app/models/people.rb
index 4069f8f..e3111b1 100644
--- a/app/models/people.rb
+++ b/app/models/people.rb
@@ -10,11 +10,21 @@ class People < ActiveRecord::Base
has_many :images, :dependent => :destroy
has_many_tagged_images
validates_uniqueness_of :title
+ has_many :friends, :foreign_key => :source_id, :dependent => :destroy
+ has_many :actual_friends, :through => :friends, :source => :destination
make_authenticatable
validates_length_of :password, :minimum => 8, :if => :password_required?,
:message => 'must be at least 8 characters in length'
+ ##
+ # Used to quickly determine if the particular id of another Person is a
+ # Friend of this instance.
+ #
+ def friend_of?(people_id)
+ Friend.count([ 'source_id = ? AND destination_id = ?', people_id, id ]) > 0
+ end
+
##
# Finds me.
#
diff --git a/app/views/friends/show.rhtml b/app/views/friends/show.rhtml
new file mode 100644
index 0000000..2235dc4
--- /dev/null
+++ b/app/views/friends/show.rhtml
@@ -0,0 +1,7 @@
+<% for friend in @friends do -%>
+
<%= link_to friend.title, people_path(friend.page.title_for_url) -%>
+<% end -%>
+
+<% content_for :sidebar do -%>
+ <%= show_people_link(@people) -%>
+<% end -%>
\ No newline at end of file
diff --git a/app/views/peoples/show.rhtml b/app/views/peoples/show.rhtml
index bd48ad3..9e491eb 100644
--- a/app/views/peoples/show.rhtml
+++ b/app/views/peoples/show.rhtml
@@ -7,4 +7,7 @@
<%= edit_people_link(@people) %>
<%= link_to 'Destroy', people_path(@people.page.title_for_url), :confirm => 'Are you sure?', :method => :delete %>
<% unless @people.tagged_images.empty? -%><%= tagged_image_browser_link -%>
<% end -%>
+ <%= show_friends_link(@people) -%>
+ <% unless @people.id == session[:people_id] or @people.friend_of?(session[:people_id]) -%><%= add_friend_link(@people) -%><% end -%>
+ <% if @people.friend_of?(session[:people_id]) -%><%= remove_friend_link(@people) -%><% end -%>
<% end -%>
\ No newline at end of file
diff --git a/config/routes.rb b/config/routes.rb
index fe9a874..4b94a67 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,8 +1,6 @@
ActionController::Routing::Routes.draw do |map|
- map.resources :tag_images
-
map.resources :beers, :breweries, :pages, :discussions, :peoples, :roles,
- :sessions, :styles, :galleries, :tag_images
+ :sessions, :styles, :galleries, :tag_images, :friends
map.connect ':controller/:action/:id.:format'
map.connect ':controller/:action/:id'
diff --git a/db/migrate/013_create_friends.rb b/db/migrate/013_create_friends.rb
new file mode 100644
index 0000000..8ef38e6
--- /dev/null
+++ b/db/migrate/013_create_friends.rb
@@ -0,0 +1,16 @@
+class CreateFriends < ActiveRecord::Migration
+ def self.up
+ create_table :friends do |t|
+ t.column :source_id, :integer
+ t.column :destination_id, :integer
+ t.column :rank, :integer
+ t.column :created_at, :timestamp
+ end
+ add_index :friends, :source_id
+ add_index :friends, :destination_id
+ end
+
+ def self.down
+ drop_table :friends
+ end
+end
diff --git a/generate_permissions b/generate_permissions
index d2ccdd8..ef031ae 100644
--- a/generate_permissions
+++ b/generate_permissions
@@ -5,7 +5,7 @@ base_actions = ApplicationController.action_methods
# rather than defining them here.
controllers = [ PagesController, DiscussionsController, StylesController,
PeoplesController, BeersController, BreweriesController, RolesController,
- GalleriesController, TagImagesController ]
+ GalleriesController, TagImagesController, FriendsController ]
controllers.each do |c|
actions = c.action_methods - base_actions
cname = c.controller_name
@@ -33,6 +33,11 @@ Permission.find(:all,
next if [ 'show', 'create', 'destroy', 'taggable_search' ].include?(p.action)
r.permissions << p
end
+Permission.find(:all,
+ :conditions => [ 'controller = ?', 'friends' ]).each do |p|
+ next if [ 'edit', 'create', 'destroy' ].include?(p.action)
+ r.permissions << p
+end
r2 = Role.admin_role
Permission.find(:all).each do |p|
diff --git a/public/images/list-add.png b/public/images/list-add.png
new file mode 100644
index 0000000..1aa7f09
Binary files /dev/null and b/public/images/list-add.png differ
diff --git a/public/images/list-remove.png b/public/images/list-remove.png
new file mode 100644
index 0000000..00b654e
Binary files /dev/null and b/public/images/list-remove.png differ
diff --git a/public/stylesheets/content.css b/public/stylesheets/content.css
index e2cbdbe..61498ce 100644
--- a/public/stylesheets/content.css
+++ b/public/stylesheets/content.css
@@ -256,7 +256,7 @@
}
/* People uploaded Image styling */
-#content .people_image {
+#content .people_image, #content .friend_box {
border: 1px solid #DDD;
background-color: #F2F2F2;
float: left;
diff --git a/test/fixtures/friends.yml b/test/fixtures/friends.yml
new file mode 100644
index 0000000..b49c4eb
--- /dev/null
+++ b/test/fixtures/friends.yml
@@ -0,0 +1,5 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+one:
+ id: 1
+two:
+ id: 2
diff --git a/test/functional/friends_controller_test.rb b/test/functional/friends_controller_test.rb
new file mode 100644
index 0000000..d6ffd05
--- /dev/null
+++ b/test/functional/friends_controller_test.rb
@@ -0,0 +1,57 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require 'friends_controller'
+
+# Re-raise errors caught by the controller.
+class FriendsController; def rescue_action(e) raise e end; end
+
+class FriendsControllerTest < Test::Unit::TestCase
+ fixtures :friends
+
+ def setup
+ @controller = FriendsController.new
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ end
+
+ def test_should_get_index
+ get :index
+ assert_response :success
+ assert assigns(:friends)
+ end
+
+ def test_should_get_new
+ get :new
+ assert_response :success
+ end
+
+ def test_should_create_friend
+ old_count = Friend.count
+ post :create, :friend => { }
+ assert_equal old_count+1, Friend.count
+
+ assert_redirected_to friend_path(assigns(:friend))
+ end
+
+ def test_should_show_friend
+ get :show, :id => 1
+ assert_response :success
+ end
+
+ def test_should_get_edit
+ get :edit, :id => 1
+ assert_response :success
+ end
+
+ def test_should_update_friend
+ put :update, :id => 1, :friend => { }
+ assert_redirected_to friend_path(assigns(:friend))
+ end
+
+ def test_should_destroy_friend
+ old_count = Friend.count
+ delete :destroy, :id => 1
+ assert_equal old_count-1, Friend.count
+
+ assert_redirected_to friends_path
+ end
+end
diff --git a/test/unit/friend_test.rb b/test/unit/friend_test.rb
new file mode 100644
index 0000000..456810c
--- /dev/null
+++ b/test/unit/friend_test.rb
@@ -0,0 +1,10 @@
+require File.dirname(__FILE__) + '/../test_helper'
+
+class FriendTest < Test::Unit::TestCase
+ fixtures :friends
+
+ # Replace this with your real tests.
+ def test_truth
+ assert true
+ end
+end