Skip to content

Commit

Permalink
new rails 3.1-compliant friend relations... all tests passing except …
Browse files Browse the repository at this point in the history
…for the

last one
  • Loading branch information
jaymcaliley committed May 21, 2011
1 parent 2a3ecb3 commit 66a8df6
Show file tree
Hide file tree
Showing 9 changed files with 131 additions and 27 deletions.
43 changes: 42 additions & 1 deletion README.md
@@ -1,6 +1,6 @@
# social_engine

social_engine is a rails engine that provides very basic social functionality that you can attach to any model, including comments, ratings, voting, favorites and user reputation. This plugin is intended to provide you with building blocks, as opposed to a full blown social networking site. Each building block is customizable and social_engine provides the controllers, helpers, models and css friendly views. You just include the helpers in your view, provide any non-default options and customize the look using CSS. This is only compatible with Rails 3 and Ruby 1.9.2.
social_engine is a rails engine that provides very basic social functionality that you can attach to any model, including comments, ratings, voting, favorites, user reputation, and friend relations. This plugin is intended to provide you with building blocks, as opposed to a full blown social networking site. Each building block is customizable and social_engine provides the controllers, helpers, models and css friendly views. You just include the helpers in your view, provide any non-default options and customize the look using CSS. This is only compatible with Rails 3 and Ruby 1.9.2.

## Installation

Expand Down Expand Up @@ -28,6 +28,7 @@ This will add the following tables to your database:
favorites
reputations
reputation_actions
friendings

## Usage

Expand Down Expand Up @@ -59,6 +60,15 @@ Make your user model social and make it have reputation
is_social
end

If you want users to also be able to 'friend' each other, just add this

class User < ActiveRecord::Base
has_reputation
is_social
is_friendable
end


## View helpers

For most situations, these helpers will suffice. The view helpers will give you forms, widgets or lists and social_engine provides controllers that will redirect to :back when you submit the forms. Using HAML for examples, but you can use the same inside <% %> for erb
Expand Down Expand Up @@ -261,6 +271,37 @@ Or if you choose not to add that field, you can get the reputation this way:

User.first.reputations.sum(:value)

### Friends
By making your User model friendable, you allow users to request friends and to confirm or reject friendship requests that come their way. The friendship model is directional, meaning it distinguishes between the friendor and the friendee. This may be useful for situations where users can "follow" others, while having many followers.

You can have @user request to be friends with @other_user
@user.add_friend(@other_user)

Get the last user who requested to be friends with @other_user
@other_user.requesting_friends.last #=> @user

You can get the same result through the Friending record for that request
@other_user.friend_requests.last.friendor #=> @user

Confirm the request
@other_user.friend_requests.last.confirm

Or reject it
@other_user.friend_requests.last.reject

If you want your app to skip the confirmation step and automatically activate all friend requests, set confirm_friends to "false" in config/yettings/social_engine.yml

You also get the following instance methods for your users
@user.friends #=> Return collection of users that are friends with @user
@user.friendors #=> Only list friends that have requested friendship with @user
@user.friendees #=> Only list friends that @user has requested friendship with

These methods return only friends that are confirmed and not rejected. To get info on unconfirmed friends:
@user.pending_friends #=> Return users that have not confirmed friendship requests from @user. This will include users that have rejected a friendship request from @user.
@user.requesting_friends #=> Return users that have requested to be friends with @user, but which @user has not confirmed
@user.rejected_friends #=> users from which @user has rejected a friendship request

The above methods are all associated with collections of Friending instances, which can be obtainer from the friendorings, friendeeings, pending_friendships, friend_requests, and friend_rejections instance methods for User.

## Development Roadmap / TODO
###Features
Expand Down
16 changes: 16 additions & 0 deletions app/models/friending.rb
Expand Up @@ -2,4 +2,20 @@ class Friending < ActiveRecord::Base
belongs_to :friendee, :class_name=>'User'
belongs_to :friendor, :class_name=>'User'
validates_uniqueness_of :friendee_id, :scope=> :friendor_id
validate :not_friending_self

def confirm
self.update_attributes(:confirmed=>true)
end

def reject
self.update_attributes(:rejected=>true)
end

private
def not_friending_self
errors.add(:friendee_id, "can't be the same as friendor_id") if
friendee_id == friendor_id
end

end
55 changes: 34 additions & 21 deletions app/models/social_engine/friendable.rb
Expand Up @@ -3,28 +3,36 @@ module Friendable
def is_friendable

# Setup conditions
confirmed = 'friendings.confirmed = ?'
rejected = 'friendings.rejected = ?'
active = ["#{confirmed} AND #{rejected}", true, false]
pending = [confirmed, false]
requested = ["#{confirmed} AND #{rejected}", false, false]
active = ["confirmed = ? AND rejected = ?", true, false]
pending = ["confirmed = ?", false]
requested = ["confirmed = ? AND rejected = ?", false, false]
rejected = ["rejected = ?", true]

# Active friendships
has_many :friendorings, :class_name=>'Friending',
:foreign_key=>:friendee_id, :conditions=>active
has_many :friendeeings, :class_name=>'Friending',
:foreign_key=>:friendor_id, :conditions=>active
# Pending friend requests from the user
has_many :pending_friendships, :class_name=>'Friending',
:foreign_key=>:friendor_id, :conditions=>pending
# Pending friend requests to the user
has_many :friend_requests, :class_name=>'Friending',
:foreign_key=>:friendee_id, :conditions=>requested
# Friend requests rejected by the user
has_many :friend_rejections, :class_name=>'Friending',
:foreign_key=>:friendee_id, :conditions=>rejected

# Friend relations (these return collections of Users)
has_many :friendors, :through=>:friendorings, :class_name=>'User'
has_many :friendees, :through=>:friendeeings, :class_name=>'User'
has_many :pending_friends, :through=>:pending_friendships,
:source=>:friendee, :class_name=>'User'
has_many :requesting_friends, :through=>:friend_requests,
:source=>:friendor, :class_name=>'User'
has_many :rejected_friends, :through=>:friend_rejections,
:source=>:friendor, :class_name=>'User'

has_and_belongs_to_many :friendors, :join_table=>:friendings,
:foreign_key=>:friendee_id, :association_foreign_key=>:friendor_id,
:conditions=>active, :class_name=>'User'
has_and_belongs_to_many :friendees, :join_table=>:friendings,
:foreign_key=>:friendor_id, :association_foreign_key=>:friendee_id,
:conditions=>active, :class_name=>'User'
has_and_belongs_to_many :pending_friends, :join_table=>:friendings,
:foreign_key=>:friendor_id, :association_foreign_key=>:friendee_id,
:conditions=>pending, :class_name=>'User'
has_and_belongs_to_many :friend_requests, :join_table=>:friendings,
:foreign_key=>:friendee_id, :association_foreign_key=>:friendor_id,
:conditions=>requested, :class_name=>'User'
has_and_belongs_to_many :rejected_friends, :join_table=>:friendings,
:foreign_key=>:friendee_id, :association_foreign_key=>:friendor_id,
:conditions=>rejected, :class_name=>'User'

include InstanceMethods
end

Expand All @@ -36,6 +44,11 @@ def friendable?
def friends
self.friendors + self.friendees
end

def add_friend(user)
Friending.create(:friendor_id=>self.id, :friendee_id=>user.id,
:confirmed=>!SocialEngineYetting.confirm_friends)
end

end
end
Expand Down
@@ -1,6 +1,6 @@
class CreateFriendingsTable < ActiveRecord::Migration
def self.up
create_table :friendings, :force => true, :id => false do |t|
create_table :friendings, :force => true do |t|
t.integer :friendor_id
t.integer :friendee_id
t.boolean :confirmed, :default => false
Expand Down
Expand Up @@ -12,6 +12,8 @@ defaults: &defaults
reputation:
update_user_model: false
user_model_rep_field_name: reputation

confirm_friends: true

tweetme:
count: horizontal
Expand Down Expand Up @@ -84,4 +86,4 @@ test:
<<: *defaults

production:
<<: *defaults
<<: *defaults
4 changes: 3 additions & 1 deletion test_app/config/yettings/social_engine.yml
Expand Up @@ -13,6 +13,8 @@ defaults: &defaults
update_user_model: false
user_model_rep_field_name: reputation

confirm_friends: true

tweetme:
count: horizontal
div_class: se-tweetme
Expand Down Expand Up @@ -84,4 +86,4 @@ test:
<<: *defaults

production:
<<: *defaults
<<: *defaults
@@ -1,6 +1,6 @@
class CreateFriendingsTable < ActiveRecord::Migration
def self.up
create_table :friendings, :force => true, :id => false do |t|
create_table :friendings, :force => true do |t|
t.integer :friendor_id
t.integer :friendee_id
t.boolean :confirmed, :default => false
Expand Down
2 changes: 1 addition & 1 deletion test_app/db/schema.rb
Expand Up @@ -19,7 +19,7 @@
t.datetime "updated_at"
end

create_table "friendings", :id => false, :force => true do |t|
create_table "friendings", :force => true do |t|
t.integer "friendor_id"
t.integer "friendee_id"
t.boolean "confirmed", :default => false
Expand Down
30 changes: 30 additions & 0 deletions test_app/spec/models/friendings_spec.rb
Expand Up @@ -34,4 +34,34 @@
jay.should have(1).friendee
jay.friendees.should include(users(:jays_friend))
end

it "should find jay's friend requests" do
jay = users(:jay)
jay.should have(1).friend_request
jay.requesting_friends.should include(users(:pending_friendor))
end

it "should find pending friend" do
user = users(:pending_friendor)
user.should have(1).pending_friend
user.pending_friends.should include(users(:jay))
end

it "should let jay friend john" do
jay = users(:jay)
john = users(:john)
jay.add_friend(john)
jay.should have(1).pending_friends
jay.pending_friends.should include(john)
john.should have(1).requesting_friends
john.requesting_friends.should include(jay)
john.friends.should_not include(jay)
puts john.friend_requests.inspect
john.friend_requests.last.confirm
puts john.friend_requests.inspect
puts john.friends.inspect
#john.should have(3).friends
john.requesting_friends.should_not include(jay)
john.friends.should include(jay)
end
end

0 comments on commit 66a8df6

Please sign in to comment.