has_friends-rails3 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Nando Vieira
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,123 @@
1
+ has_friends
2
+ ===========
3
+
4
+ **ATTENTION:** This is a Rails 3 implementation.
5
+
6
+ NOTE: You should have a User model. When you execute the plugin generator, I'll add a column
7
+ friends_count on users' table.
8
+
9
+
10
+ Instalation
11
+ -----------
12
+
13
+ 1) Add a gem line to your Gemfile. `gem 'has_friends-rails3, :require => 'has_friends'`
14
+
15
+ 2) Execute the plugin generator `rails g has_friends:migration`
16
+
17
+ 3) Run the migrations with `rake db:migrate`
18
+
19
+ Usage
20
+ -----
21
+
22
+ 1) Add the method call `has_friends` to your model.
23
+
24
+ class User < ActiveRecord::Base
25
+ has_friends
26
+ end
27
+
28
+ john = User.find_by_login 'john'
29
+ mary = User.find_by_login 'mary'
30
+ paul = User.find_by_login 'paul'
31
+
32
+ # John wants to be friend with Mary
33
+ # always return a friendship object
34
+ john.be_friends_with(mary, "Hi Mary! I have worked with you on Meroy Merlin!")
35
+
36
+ # Creating a new Relation kind
37
+ RelationType.create(:name => "coworker")
38
+
39
+ # You can pass kind of relationship, when...
40
+ john.be_friends_with(mary, "Hi Mary! I have worked with you on Meroy Merlin!", [:coworker])
41
+
42
+ # if you specify a non existent Relation,
43
+ # it will create a new Relation
44
+ # and associate then to friendship
45
+ john.be_friends_with(mary, "Hi Mary! I have worked with you on Meroy Merlin!", [:coworker, :friend])
46
+ # In this case, friend is a new relation
47
+
48
+ # are they friends?
49
+ john.friends?(mary) ==> false
50
+
51
+ # get the friendship object
52
+ john.friendship_for(mary)
53
+
54
+ # Mary accepts John's request if it exists...
55
+ mary.accept_friendship_with(john)
56
+ mary.friends?(john) ==> true
57
+
58
+ # either if Mary request John's friendship, then they will be friends automatically.
59
+ mary.be_friends_with(john)
60
+ mary.friends?(john) ==> true
61
+
62
+ # Mary can reject John's friendship.
63
+ mary.remove_friendship_with(john)
64
+
65
+ # check if an user is the current user, so it can
66
+ # be differently presented
67
+ mary.friends.each {|friend| friend.is?(current_user) }
68
+
69
+ # if you're dealing with a friendship object,
70
+ # the following methods are available
71
+ friendship.accept!
72
+
73
+ # You can specify relations when accept a friendship, so...
74
+ friendship.accept!([:friend])
75
+
76
+ # If you want to accept friendship and clear current relations...
77
+ friendship.accept!([])
78
+
79
+ # the be_friends_with method returns 2 params: friendship object and status.
80
+ # the friendship object will be present only when the friendship is created
81
+ # (that is, when is requested for the first time)
82
+ # STATUS_ALREADY_FRIENDS # => users are already friends
83
+ # STATUS_ALREADY_REQUESTED # => user has already requested friendship
84
+ # STATUS_IS_YOU # => user is trying add himself as friend
85
+ # STATUS_FRIEND_IS_REQUIRED # => friend argument is missing
86
+ # STATUS_FRIENDSHIP_ACCEPTED # => friendship has been accepted
87
+ # STATUS_REQUESTED # => friendship has been requested
88
+
89
+ friendship, status = mary.be_friends_with(john)
90
+
91
+ if status == Friendship::STATUS_REQUESTED
92
+ # the friendship has been requested
93
+ Mailer.deliver_friendship_request(friendship)
94
+ elsif status == Friendship::STATUS_ALREADY_FRIENDS
95
+ # they're already friends
96
+ else
97
+ # ...
98
+ end
99
+
100
+
101
+ LICENSE:
102
+ --------
103
+
104
+ (The MIT License)
105
+
106
+ Permission is hereby granted, free of charge, to any person obtaining
107
+ a copy of this software and associated documentation files (the
108
+ 'Software'), to deal in the Software without restriction, including
109
+ without limitation the rights to use, copy, modify, merge, publish,
110
+ distribute, sublicense, and/or sell copies of the Software, and to
111
+ permit persons to whom the Software is furnished to do so, subject to
112
+ the following conditions:
113
+
114
+ The above copyright notice and this permission notice shall be
115
+ included in all copies or substantial portions of the Software.
116
+
117
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
118
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
119
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
120
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
121
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
122
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
123
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/RakeFile ADDED
@@ -0,0 +1,25 @@
1
+ if defined?(Rspec)
2
+ require 'spec/rake/spectask'
3
+
4
+ namespace :has_friends do
5
+ desc "Run all specs in spec directory (excluding plugin specs)"
6
+ Spec::Rake::SpecTask.new(:spec) do |t|
7
+ t.spec_files = FileList["spec/**/*_spec.rb"]
8
+ end
9
+ end
10
+ end
11
+
12
+ begin
13
+ require 'jeweler'
14
+ Jeweler::Tasks.new do |gemspec|
15
+ gemspec.name = "has_friends-rails3"
16
+ gemspec.summary = "Rails 3 version of has_friends gem"
17
+ gemspec.description = "Rails 3 version of has_friends gem"
18
+ gemspec.email = "rrouse@rachotech.com"
19
+ gemspec.homepage = "http://github.com/rrouse/has_friends"
20
+ gemspec.authors = ["Robert Rouse"]
21
+ gemspec.files.include %w(lib/**/*.rb)
22
+ end
23
+ rescue LoadError
24
+ puts "Jeweler not available. Install it with: gem install jeweler"
25
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.3
@@ -0,0 +1,72 @@
1
+ class Friendship < ActiveRecord::Base
2
+ # constants
3
+ STATUS_ALREADY_FRIENDS = 1
4
+ STATUS_ALREADY_REQUESTED = 2
5
+ STATUS_IS_YOU = 3
6
+ STATUS_FRIEND_IS_REQUIRED = 4
7
+ STATUS_FRIENDSHIP_ACCEPTED = 5
8
+ STATUS_REQUESTED = 6
9
+
10
+ FRIENDSHIP_ACCEPTED = "accepted"
11
+ FRIENDSHIP_PENDING = "pending"
12
+ FRIENDSHIP_REQUESTED = "requested"
13
+
14
+ # scopes
15
+ scope :pending, :conditions => {:status => FRIENDSHIP_PENDING}
16
+ scope :accepted, :conditions => {:status => FRIENDSHIP_ACCEPTED}
17
+ scope :requested, :conditions => {:status => FRIENDSHIP_REQUESTED}
18
+
19
+ # associations
20
+ belongs_to :user
21
+ belongs_to :friend, :class_name => 'User', :foreign_key => 'friend_id'
22
+ belongs_to :message, :class_name => "FriendshipMessage", :foreign_key => "friendship_message_id", :dependent => :destroy
23
+
24
+ has_many :friendship_relations, :class_name => "FriendshipRelationType", :readonly => true, :dependent => :destroy
25
+ has_many :relations, :through => :friendship_relations, :class_name => "RelationType", :source => :relation
26
+
27
+ # callback
28
+ after_destroy do |f|
29
+ User.decrement_counter(:friends_count, f.user_id) if f.status == FRIENDSHIP_ACCEPTED
30
+ end
31
+
32
+ def pending?
33
+ status == FRIENDSHIP_PENDING
34
+ end
35
+
36
+ def accepted?
37
+ status == FRIENDSHIP_ACCEPTED
38
+ end
39
+
40
+ def requested?
41
+ status == FRIENDSHIP_REQUESTED
42
+ end
43
+
44
+ def accept!(new_relation_names = nil)
45
+ unless accepted?
46
+ self.transaction do
47
+ User.increment_counter(:friends_count, user_id)
48
+ update_attribute(:status, FRIENDSHIP_ACCEPTED)
49
+ update_attribute(:accepted_at, Time.now)
50
+ add_relations(new_relation_names) unless new_relation_names.nil?
51
+ end
52
+ end
53
+ end
54
+
55
+ def add_relations(new_relation_names = [])
56
+ self.transaction do
57
+ actual_relation_names = self.relation_names
58
+ self.friendship_relations.each do |fr|
59
+ fr.destroy unless actual_relation_names.include?(fr.relation.name.to_sym)
60
+ end
61
+
62
+ new_relation_names.each do |nr|
63
+ relation = RelationType.find_or_create_by_name(nr.to_s)
64
+ self.relations << relation unless relations.include?(relation)
65
+ end
66
+ end
67
+ end
68
+
69
+ def relation_names
70
+ relations.all(:select => :name, :order => :name).collect{|r| r.name}
71
+ end
72
+ end
@@ -0,0 +1,3 @@
1
+ class FriendshipMessage < ActiveRecord::Base
2
+ validates :body, :presence => true
3
+ end
@@ -0,0 +1,4 @@
1
+ class FriendshipRelationType < ActiveRecord::Base
2
+ belongs_to :friendship
3
+ belongs_to :relation, :class_name => "RelationType"
4
+ end
@@ -0,0 +1,10 @@
1
+ class RelationType < ActiveRecord::Base
2
+ validates :name, :presence => true
3
+
4
+ after_destroy :destroy_all_friendsip_relations
5
+
6
+ private
7
+ def destroy_all_friendsip_relations
8
+ FriendshipRelationType.delete_all :relation_id => self.id
9
+ end
10
+ end
@@ -0,0 +1,70 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in RakeFile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{has_friends-rails3}
8
+ s.version = "0.0.3"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Robert Rouse"]
12
+ s.date = %q{2010-07-25}
13
+ s.description = %q{Rails 3 version of has_friends gem}
14
+ s.email = %q{rrouse@rachotech.com}
15
+ s.extra_rdoc_files = [
16
+ "README.markdown"
17
+ ]
18
+ s.files = [
19
+ "MIT-LICENSE",
20
+ "README.markdown",
21
+ "RakeFile",
22
+ "VERSION",
23
+ "app/models/friendship.rb",
24
+ "app/models/friendship_message.rb",
25
+ "app/models/friendship_relation_type.rb",
26
+ "app/models/relation_type.rb",
27
+ "has_friends-rails3.gemspec",
28
+ "lib/generators/has_friends.rb",
29
+ "lib/generators/has_friends/migration/migration_generator.rb",
30
+ "lib/generators/has_friends/migration/templates/create_has_friends_tables.rb",
31
+ "lib/has_friends.rb",
32
+ "lib/has_friends/engine.rb",
33
+ "lib/has_friends/exceptions.rb",
34
+ "lib/has_friends/has_friends.rb",
35
+ "spec/fixtures/relation_types.yml",
36
+ "spec/fixtures/users.yml",
37
+ "spec/friendship_message_spec.rb",
38
+ "spec/friendship_relation_type_spec.rb",
39
+ "spec/friendship_spec.rb",
40
+ "spec/has_friends_spec.rb",
41
+ "spec/relation_type_spec.rb",
42
+ "spec/schema.rb",
43
+ "spec/spec_helper.rb"
44
+ ]
45
+ s.homepage = %q{http://github.com/rrouse/has_friends}
46
+ s.rdoc_options = ["--charset=UTF-8"]
47
+ s.require_paths = ["lib"]
48
+ s.rubygems_version = %q{1.3.7}
49
+ s.summary = %q{Rails 3 version of has_friends gem}
50
+ s.test_files = [
51
+ "spec/friendship_message_spec.rb",
52
+ "spec/friendship_relation_type_spec.rb",
53
+ "spec/friendship_spec.rb",
54
+ "spec/has_friends_spec.rb",
55
+ "spec/relation_type_spec.rb",
56
+ "spec/schema.rb",
57
+ "spec/spec_helper.rb"
58
+ ]
59
+
60
+ if s.respond_to? :specification_version then
61
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
62
+ s.specification_version = 3
63
+
64
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
65
+ else
66
+ end
67
+ else
68
+ end
69
+ end
70
+
@@ -0,0 +1,14 @@
1
+ require 'generators/has_friends'
2
+
3
+ module HasFriends
4
+ module Generators
5
+ class MigrationGenerator < Base
6
+ desc "Generate has_friends migration"
7
+
8
+ def create_migration_file
9
+ migration_template "create_has_friends_tables.rb", File.join('db', 'migrate', "create_has_friends_tables.rb")
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,42 @@
1
+ class CreateHasFriendsTables < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :friendships, :force => true do |t|
4
+ t.references :user, :friend, :friendship_message
5
+ t.datetime :requested_at, :accepted_at
6
+ t.string :status
7
+ t.timestamps
8
+ end
9
+
10
+ add_index :friendships, :user_id
11
+ add_index :friendships, :friend_id
12
+ add_index :friendships, :status
13
+
14
+ create_table :relation_types, :force => true do |t|
15
+ t.string :name
16
+ t.timestamps
17
+ end
18
+
19
+ add_index :relation_types, :name
20
+
21
+ create_table :friendship_messages, :force => true do |t|
22
+ t.string :body
23
+ t.timestamps
24
+ end
25
+
26
+ create_table :friendship_relation_types, :force => true do |t|
27
+ t.references :relation, :friendship
28
+ t.timestamps
29
+ end
30
+
31
+ add_column :users, :friends_count, :integer, :default => 0, :null => false
32
+ end
33
+
34
+ def self.down
35
+ remove_column :users, :friends_count
36
+
37
+ drop_table :friendship_relation_types
38
+ drop_table :friendship_messages
39
+ drop_table :relation_types
40
+ drop_table :friendships
41
+ end
42
+ end
@@ -0,0 +1,19 @@
1
+ require 'rails/generators/migration'
2
+
3
+ module HasFriends
4
+ module Generators
5
+ class Base < Rails::Generators::Base
6
+
7
+ include Rails::Generators::Migration
8
+
9
+ def self.source_root
10
+ @_has_friends_source_root ||= File.expand_path(File.join(File.dirname(__FILE__), 'has_friends', generator_name, 'templates'))
11
+ end
12
+
13
+ def self.next_migration_number(path)
14
+ Time.now.utc.strftime("%Y%m%d%H%M%S")
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,10 @@
1
+ require 'has_friends'
2
+ require 'rails'
3
+
4
+ module Has
5
+ module Friends
6
+ class Engine < Rails::Engine
7
+
8
+ end
9
+ end
10
+ end
@@ -0,0 +1 @@
1
+ class YouCanNotJudgeARequestFriendshipError < StandardError; end
@@ -0,0 +1,118 @@
1
+ module Has
2
+ module Friends
3
+ def self.included(base)
4
+ base.extend Has::Friends::ClassMethods
5
+ end
6
+
7
+ module ClassMethods
8
+ def has_friends
9
+ include Has::Friends::InstanceMethods
10
+
11
+ has_many :friendships
12
+ has_many :friends, :through => :friendships, :source => :friend, :conditions => "friendships.status = 'accepted'"
13
+
14
+ after_destroy :destroy_all_friendships
15
+ end
16
+ end
17
+
18
+ module InstanceMethods
19
+ def be_friends_with(friend, message = nil, relations = [])
20
+ # no user object
21
+ return nil, Friendship::STATUS_FRIEND_IS_REQUIRED unless friend
22
+
23
+ # should not create friendship if user is trying to add himself
24
+ return nil, Friendship::STATUS_IS_YOU if is?(friend)
25
+
26
+ # should not create friendship if users are already friends
27
+ return nil, Friendship::STATUS_ALREADY_FRIENDS if friends?(friend)
28
+
29
+ # retrieve the friendship request
30
+ friendship = self.friendship_for(friend)
31
+
32
+ # let's check if user has already a friendship request or have removed
33
+ request = friend.friendship_for(self)
34
+
35
+ # friendship has already been requested
36
+ return nil, Friendship::STATUS_ALREADY_REQUESTED if friendship && friendship.requested?
37
+
38
+ # friendship is pending so accept it
39
+ if friendship && friendship.pending?
40
+ ActiveRecord::Base.transaction do
41
+ friendship.accept!
42
+ request.accept!
43
+ end
44
+
45
+ return friendship, Friendship::STATUS_FRIENDSHIP_ACCEPTED
46
+ end
47
+
48
+ ActiveRecord::Base.transaction do
49
+ message = FriendshipMessage.create(:body => message) if message
50
+
51
+ # we didn't find a friendship, so let's create one!
52
+ friendship = self.friendships.create(:friend_id => friend.id, :status => 'requested', :message => message, :requested_at => Time.now)
53
+ friendship.add_relations(relations)
54
+
55
+ # we didn't find a friendship request, so let's create it!
56
+ request = friend.friendships.create(:friend_id => id, :status => 'pending', :message => message, :requested_at => Time.now)
57
+ request.add_relations(relations)
58
+ end
59
+
60
+ return friendship, Friendship::STATUS_REQUESTED
61
+ end
62
+
63
+ def friends?(friend)
64
+ friendship = friendship_for(friend)
65
+ !!(friendship && friendship.accepted?)
66
+ end
67
+
68
+ def friendship_for(friend)
69
+ friendships.first :conditions => {:friend_id => friend.id}
70
+ end
71
+
72
+ def is?(friend)
73
+ self.id == friend.id
74
+ end
75
+
76
+ def remove_friendship_with(friend)
77
+ ActiveRecord::Base.transaction do
78
+ [friendship_for(friend), friend.friendship_for(self)].compact.each do |friendship|
79
+ friendship.destroy if friendship
80
+ end
81
+ end
82
+ end
83
+
84
+ def deny_friendship_with(friend)
85
+ if (pendent = self.friendship_for(friend)).pending?
86
+ ActiveRecord::Base.transaction do
87
+ [friendship_for(friend), friend.friendship_for(self)].compact.each do |friendship|
88
+ friendship.destroy if friendship
89
+ end
90
+ end
91
+ else
92
+ raise YouCanNotJudgeARequestFriendshipError
93
+ end
94
+ end
95
+
96
+ def accept_friendship_with(friend, relations = nil)
97
+ if (pendent = self.friendship_for(friend)).pending?
98
+ requested = friend.friendship_for(self)
99
+
100
+ ActiveRecord::Base.transaction do
101
+ pendent.accept!(relations)
102
+ requested.accept! unless requested.accepted?
103
+ end
104
+ else
105
+ raise YouCanNotJudgeARequestFriendshipError
106
+ end
107
+ end
108
+
109
+ private
110
+ def destroy_all_friendships
111
+ ActiveRecord::Base.transaction do
112
+ Friendship.delete_all({:user_id => id})
113
+ Friendship.delete_all({:friend_id => id})
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,5 @@
1
+ require 'has_friends/engine'
2
+ require 'has_friends/has_friends'
3
+ require 'has_friends/exceptions'
4
+
5
+ ActiveRecord::Base.send(:include, Has::Friends)
@@ -0,0 +1,8 @@
1
+ friend:
2
+ name: friend
3
+
4
+ met:
5
+ name: met
6
+
7
+ coworker:
8
+ name: coworker
@@ -0,0 +1,14 @@
1
+ vader:
2
+ login: darth_vader
3
+
4
+ luke:
5
+ login: luke_skywalker
6
+
7
+ leia:
8
+ login: princess_leia
9
+
10
+ han_solo:
11
+ login: han_solo
12
+
13
+ yoda:
14
+ login: yoda
@@ -0,0 +1,10 @@
1
+ require "#{File.dirname(__FILE__)}/spec_helper"
2
+
3
+ describe FriendshipMessage do
4
+ it "should require a body" do
5
+ @message = FriendshipMessage.new
6
+
7
+ @message.should_not be_valid
8
+ @message.errors[:body].should == "can't be blank"
9
+ end
10
+ end
@@ -0,0 +1,17 @@
1
+ require "#{File.dirname(__FILE__)}/spec_helper"
2
+
3
+ describe FriendshipRelationType do
4
+ describe "structure" do
5
+ it "should belong_to :friendship" do
6
+ @friendship = Friendship.new
7
+ @friendship_relation = FriendshipRelationType.new :friendship => @friendship
8
+ @friendship_relation.friendship.should == @friendship
9
+ end
10
+
11
+ it "should belong_to :relation" do
12
+ @relation = RelationType.new
13
+ @friendship_relation = FriendshipRelationType.new :relation => @relation
14
+ @friendship_relation.relation.should == @relation
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,113 @@
1
+ require "#{File.dirname(__FILE__)}/spec_helper"
2
+
3
+ describe Friendship do
4
+ fixtures :all
5
+
6
+ describe "structure" do
7
+ before(:each) do
8
+ @friendship = Friendship.new
9
+ end
10
+
11
+ it "should belong to user" do
12
+ @friendship.user = @vader
13
+ @friendship.user.should == @vader
14
+ end
15
+
16
+ it "should_belong to friend" do
17
+ @friendship.friend = @luke
18
+ @friendship.friend.should == @luke
19
+ end
20
+
21
+ it "should belong to message" do
22
+ @message = FriendshipMessage.new :body => "Luke, I'm your father!"
23
+ @friendship.message = @vader_message_for_luke
24
+ @friendship.message.should == @vader_message_for_luke
25
+ end
26
+
27
+ it "should has many relations" do
28
+ @friendship.relations << @friend
29
+ @friendship.relations << @coworker
30
+
31
+ @friendship.relations.should == [@friend, @coworker]
32
+ end
33
+
34
+ it "should list relations names" do
35
+ @friendship.relations << @friend
36
+ @friendship.relations << @coworker
37
+ @friendship.save
38
+ @friendship.reload
39
+
40
+ @friendship.relation_names.should == ["coworker", "friend"]
41
+ end
42
+ end
43
+
44
+ it "should be pending status" do
45
+ @friendship = Friendship.new(:status => 'pending')
46
+ @friendship.should be_pending
47
+ end
48
+
49
+ it "should be accepted status" do
50
+ @friendship = Friendship.new(:status => 'accepted')
51
+ @friendship.should be_accepted
52
+ end
53
+
54
+ it "should be requested status" do
55
+ @friendship = Friendship.new(:status => 'requested')
56
+ @friendship.should be_requested
57
+ end
58
+
59
+ describe "on accept pending friendship" do
60
+ before(:each) do
61
+ @friendship = Friendship.create(:user => @vader, :friend => @luke, :status => Friendship::FRIENDSHIP_PENDING)
62
+ end
63
+
64
+ it "should be ok" do
65
+ @friendship.accept!
66
+ @friendship.status.should == Friendship::FRIENDSHIP_ACCEPTED
67
+ end
68
+
69
+ it "should be ok and add new relations" do
70
+ @friendship.accept!([:met, :coworker])
71
+ @friendship.status.should == Friendship::FRIENDSHIP_ACCEPTED
72
+ @friendship.relations.size == 2
73
+ @friendship.relations.should == [@met, @coworker]
74
+ end
75
+
76
+ describe "with relations" do
77
+ before(:each) do
78
+ [@friend, @met].each do |r|
79
+ @friendship.relations << r
80
+ end
81
+ @friendship.save
82
+ end
83
+
84
+ it "should be ok and no change current relations" do
85
+ @friendship.accept!
86
+ @friendship.reload
87
+
88
+ @friendship.status.should == Friendship::FRIENDSHIP_ACCEPTED
89
+ @friendship.relations.size.should == 2
90
+ @friendship.relations.should == [@friend, @met]
91
+ end
92
+
93
+ it "should be ok and change relations" do
94
+ @friendship.accept!([:met, :coworker])
95
+ @friendship.reload
96
+
97
+ @friendship.status.should == Friendship::FRIENDSHIP_ACCEPTED
98
+ @friendship.relations.size.should == 2
99
+ @friendship.relations.should == [@met, @coworker]
100
+ end
101
+
102
+ it "should be ok and clear current relations" do
103
+ @friendship.accept!([])
104
+ @friendship.reload
105
+
106
+ @friendship.status.should == Friendship::FRIENDSHIP_ACCEPTED
107
+ @friendship.relations.size.should == 0
108
+ @friendship.relations.should == []
109
+ end
110
+ end
111
+
112
+ end
113
+ end
@@ -0,0 +1,252 @@
1
+ require "#{File.dirname(__FILE__)}/spec_helper"
2
+
3
+ describe "has_friends" do
4
+ fixtures :all
5
+
6
+ describe "methods" do
7
+ it "should respond to has_friends method" do
8
+ User.should respond_to(:has_friends)
9
+ end
10
+
11
+ it "should respond to be_friends_with method" do
12
+ @vader.should respond_to(:be_friends_with)
13
+ end
14
+
15
+ it "should respond to friends? method" do
16
+ @vader.should respond_to(:friends?)
17
+ end
18
+
19
+ it "should respond to accept_friendship_with" do
20
+ @vader.should respond_to(:accept_friendship_with)
21
+ end
22
+
23
+ it "should respond to remove_friendship_with" do
24
+ @vader.should respond_to(:remove_friendship_with)
25
+ end
26
+ end
27
+
28
+ describe "user" do
29
+ before(:each) do
30
+ #requesting friendship between @vader and @luke
31
+ @vader.be_friends_with(@luke)
32
+ @leia.be_friends_with(@luke)
33
+ @luke.accept_friendship_with(@leia)
34
+ end
35
+
36
+ it "should accept a friendship" do
37
+ @luke.accept_friendship_with(@vader)
38
+
39
+ @luke.reload
40
+ @vader.reload
41
+
42
+ @vader.friends.should == [@luke]
43
+ @luke.friends.should == [@vader, @leia]
44
+ @vader.friends_count.should == 1
45
+ @luke.friends_count.should == 2
46
+ end
47
+
48
+ it "should accept a friendship with relation types" do
49
+ doing {
50
+ @luke.accept_friendship_with(@vader, [:parent, :met])
51
+ }.should change(FriendshipRelationType, :count).by(2)
52
+ end
53
+
54
+ it "should reject when user try accept his friendship request" do
55
+ lambda { @vader.accept_friendship_with(@luke) }.should raise_error(YouCanNotJudgeARequestFriendshipError)
56
+
57
+ @luke.reload
58
+ @vader.reload
59
+
60
+ @vader.friends.should == []
61
+ @luke.friends.should == [@leia]
62
+ @vader.friends_count.should == 0
63
+ @luke.friends_count.should == 1
64
+ end
65
+
66
+ it "should accept a friendship with relation types" do
67
+ @luke.deny_friendship_with(@vader)
68
+
69
+ @luke.reload
70
+ @vader.reload
71
+
72
+ @vader.friends.should == []
73
+ @luke.friends.should == [@leia]
74
+ @vader.friends_count.should == 0
75
+ @luke.friends_count.should == 1
76
+ end
77
+
78
+ it "should reject when user try deny his friendship request" do
79
+ lambda { @vader.deny_friendship_with(@luke) }.should raise_error(YouCanNotJudgeARequestFriendshipError)
80
+
81
+ @luke.reload
82
+ @vader.reload
83
+
84
+ @vader.friends.should == []
85
+ @luke.friends.should == [@leia]
86
+ @vader.friends_count.should == 0
87
+ @luke.friends_count.should == 1
88
+ end
89
+
90
+ it "should remove a friendship" do
91
+ #make @vader and @luke friends
92
+ @luke.be_friends_with(@vader)
93
+
94
+ @vader.remove_friendship_with(@luke)
95
+
96
+ @luke.reload
97
+ @vader.reload
98
+
99
+ @vader.friends.should == []
100
+ @luke.friends.should == [@leia]
101
+ @vader.friends_count.should == 0
102
+ @luke.friends_count.should == 1
103
+ end
104
+ end
105
+
106
+ describe "friends" do
107
+ before(:each) do
108
+ create_friendship @vader, @luke
109
+ create_friendship @vader, @leia
110
+ create_friendship @luke, @leia
111
+ create_friendship @luke, @yoda
112
+ create_friendship @leia, @han_solo
113
+ end
114
+
115
+ it "should order vader's friends" do
116
+ # => princess_leia, luke_skywalker
117
+ @vader.friends.all(:order => 'login desc').should == [@leia, @luke]
118
+ end
119
+
120
+ it "should return luke's friends" do
121
+ @luke.friends.should == [@vader, @leia, @yoda]
122
+ end
123
+
124
+ it "should return leia's frieds" do
125
+ @leia.friends.should == [@vader, @luke, @han_solo]
126
+ end
127
+
128
+ it "should return yoda's friends" do
129
+ @yoda.friends.should == [@luke]
130
+ end
131
+
132
+ it "should return solo's friends" do
133
+ @han_solo.friends.should == [@leia]
134
+ end
135
+
136
+ it "should increment counter" do
137
+ @vader.reload
138
+ @vader.friends_count.should == 2
139
+ end
140
+
141
+ it "should decrement counter" do
142
+ friendship = @vader.friendship_for(@luke)
143
+ friendship.destroy
144
+
145
+ @vader.reload
146
+ @vader.friends_count.should == 1
147
+ end
148
+
149
+ it "should be @vader" do
150
+ @vader.is?(@vader).should be_true
151
+ end
152
+
153
+ it "should not be @vader" do
154
+ @vader.is?(@leia).should_not be_true
155
+ end
156
+ end
157
+
158
+ describe "friendship request" do
159
+ it "should return nil and 'friend is required' status" do
160
+ friendship, status = @vader.be_friends_with(nil)
161
+
162
+ friendship.should be_nil
163
+ status.should == Friendship::STATUS_FRIEND_IS_REQUIRED
164
+ end
165
+
166
+ it "should return nil and 'is you' status" do
167
+ friendship, status = @vader.be_friends_with(@vader)
168
+
169
+ friendship.should be_nil
170
+ status.should == Friendship::STATUS_IS_YOU
171
+ end
172
+
173
+ it "should return nil and 'already friends status" do
174
+ @vader.be_friends_with(@luke)
175
+ @luke.be_friends_with(@vader)
176
+ friendship, status = @vader.be_friends_with(@luke)
177
+
178
+ friendship.should be_nil
179
+ status.should == Friendship::STATUS_ALREADY_FRIENDS
180
+ end
181
+
182
+ it "should return nil and 'already requested' status" do
183
+ @vader.be_friends_with(@luke)
184
+ friendship, status = @vader.be_friends_with(@luke)
185
+
186
+ friendship.should be_nil
187
+ status.should == Friendship::STATUS_ALREADY_REQUESTED
188
+ end
189
+
190
+ it "should return friendship and 'accepted friendship' status" do
191
+ @vader.be_friends_with(@luke)
192
+ friendship, status = @luke.be_friends_with(@vader)
193
+
194
+ friendship.should be_kind_of(Friendship)
195
+ status.should == Friendship::STATUS_FRIENDSHIP_ACCEPTED
196
+ @vader.should be_friends(@luke)
197
+ @luke.should be_friends(@vader)
198
+ end
199
+
200
+ it "should create friendships" do
201
+ doing {
202
+ friendship, status = @vader.be_friends_with(@luke)
203
+
204
+ @vader.friendships.count.should == 1
205
+ @luke.friendships.count.should == 1
206
+ status.should == Friendship::STATUS_REQUESTED
207
+ }.should change(Friendship, :count).by(2)
208
+ end
209
+
210
+ it "should create friendship with message" do
211
+ doing {
212
+ friendship, status = @vader.be_friends_with(@luke, "Luke, I'm your father!")
213
+ friendship.message.body.should == "Luke, I'm your father!"
214
+ }.should change(FriendshipMessage, :count).by(1)
215
+ end
216
+
217
+ it "should create friendship with exist relations" do
218
+ doing {
219
+ doing {
220
+ friendship, status = @vader.be_friends_with(@luke, nil, [:met, :friend])
221
+ }.should change(FriendshipRelationType, :count).by(4)
222
+ }.should_not change(RelationType, :count)
223
+ end
224
+
225
+ it "should create friendship with new relations" do
226
+ doing {
227
+ friendship, status = @vader.be_friends_with(@luke, nil, [:parent, :met])
228
+ }.should change(RelationType, :count).by(1)
229
+ end
230
+ end
231
+
232
+ describe "pagination" do
233
+ before(:each) do
234
+ pending unless Object.const_defined?('Paginate')
235
+
236
+ create_friendship @vader, @luke
237
+ create_friendship @vader, @leia
238
+ create_friendship @vader, @han_solo
239
+ end
240
+
241
+ it "should paginate friends" do
242
+ @vader.friends.paginate(:limit => 1).to_a.should == [@luke, @leia]
243
+ end
244
+ end
245
+
246
+ private
247
+ def create_friendship(user1, user2)
248
+ user1.be_friends_with(user2)
249
+ user2.be_friends_with(user1)
250
+ end
251
+
252
+ end
@@ -0,0 +1,9 @@
1
+ require "#{File.dirname(__FILE__)}/spec_helper"
2
+
3
+ describe RelationType do
4
+ it "should require a name" do
5
+ @relation_type = RelationType.new
6
+ @relation_type.should_not be_valid
7
+ @relation_type.errors[:name].should == "can't be blank"
8
+ end
9
+ end
data/spec/schema.rb ADDED
@@ -0,0 +1,37 @@
1
+ ActiveRecord::Schema.define(:version => 0) do
2
+ begin
3
+ drop_table :users
4
+ drop_table :friendships
5
+ drop_table :friendship_messages
6
+ drop_table :relations_types
7
+ drop_table :friendship_relations
8
+ rescue
9
+ end
10
+
11
+ create_table :users do |t|
12
+ t.string :login
13
+ t.integer :friends_count, :default => 0, :null => false
14
+ end
15
+
16
+ create_table :friendships do |t|
17
+ t.references :user, :friend, :friendship_message
18
+ t.string :status
19
+ t.timestamps
20
+ end
21
+
22
+ create_table :friendship_messages do |t|
23
+ t.string :body
24
+ t.timestamps
25
+ end
26
+
27
+ create_table :relation_types do |t|
28
+ t.string :name
29
+ t.timestamps
30
+ end
31
+
32
+ create_table :friendship_relation_types do |t|
33
+ t.references :relation, :friendship
34
+ t.timestamps
35
+ end
36
+
37
+ end
@@ -0,0 +1,37 @@
1
+ ENV["RAILS_ENV"] = "test"
2
+
3
+ require File.expand_path(File.join(File.dirname(__FILE__), "../../../../config/environment"))
4
+ require "spec"
5
+ require "spec/rails"
6
+ require "ruby-debug"
7
+
8
+ ActiveRecord::Base.configurations = {'test' => {:adapter => 'sqlite3', :database => ":memory:"}}
9
+ ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations["test"])
10
+
11
+ load(File.join(File.dirname(__FILE__), 'schema.rb'))
12
+
13
+ Spec::Runner.configure do |config|
14
+ config.use_transactional_fixtures = true
15
+ config.use_instantiated_fixtures = true
16
+ config.fixture_path = File.dirname(__FILE__) + '/fixtures/'
17
+ end
18
+
19
+ class Object
20
+ def self.unset_class(*args)
21
+ class_eval do
22
+ args.each do |klass|
23
+ eval(klass) rescue nil
24
+ remove_const(klass) if const_defined?(klass)
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ alias :doing :lambda
31
+
32
+ # unset models used for testing purposes
33
+ Object.unset_class('User')
34
+
35
+ class User < ActiveRecord::Base
36
+ has_friends
37
+ end
metadata ADDED
@@ -0,0 +1,94 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: has_friends-rails3
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 3
9
+ version: 0.0.3
10
+ platform: ruby
11
+ authors:
12
+ - Robert Rouse
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-07-25 00:00:00 -05:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: Rails 3 version of has_friends gem
22
+ email: rrouse@rachotech.com
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files:
28
+ - README.markdown
29
+ files:
30
+ - MIT-LICENSE
31
+ - README.markdown
32
+ - RakeFile
33
+ - VERSION
34
+ - app/models/friendship.rb
35
+ - app/models/friendship_message.rb
36
+ - app/models/friendship_relation_type.rb
37
+ - app/models/relation_type.rb
38
+ - has_friends-rails3.gemspec
39
+ - lib/generators/has_friends.rb
40
+ - lib/generators/has_friends/migration/migration_generator.rb
41
+ - lib/generators/has_friends/migration/templates/create_has_friends_tables.rb
42
+ - lib/has_friends.rb
43
+ - lib/has_friends/engine.rb
44
+ - lib/has_friends/exceptions.rb
45
+ - lib/has_friends/has_friends.rb
46
+ - spec/fixtures/relation_types.yml
47
+ - spec/fixtures/users.yml
48
+ - spec/friendship_message_spec.rb
49
+ - spec/friendship_relation_type_spec.rb
50
+ - spec/friendship_spec.rb
51
+ - spec/has_friends_spec.rb
52
+ - spec/relation_type_spec.rb
53
+ - spec/schema.rb
54
+ - spec/spec_helper.rb
55
+ has_rdoc: true
56
+ homepage: http://github.com/rrouse/has_friends
57
+ licenses: []
58
+
59
+ post_install_message:
60
+ rdoc_options:
61
+ - --charset=UTF-8
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ segments:
70
+ - 0
71
+ version: "0"
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ segments:
78
+ - 0
79
+ version: "0"
80
+ requirements: []
81
+
82
+ rubyforge_project:
83
+ rubygems_version: 1.3.7
84
+ signing_key:
85
+ specification_version: 3
86
+ summary: Rails 3 version of has_friends gem
87
+ test_files:
88
+ - spec/friendship_message_spec.rb
89
+ - spec/friendship_relation_type_spec.rb
90
+ - spec/friendship_spec.rb
91
+ - spec/has_friends_spec.rb
92
+ - spec/relation_type_spec.rb
93
+ - spec/schema.rb
94
+ - spec/spec_helper.rb