amistad 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/CHANGELOG ADDED
@@ -0,0 +1 @@
1
+ A history of changes to the gem...
data/LICENCE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Rawane ZOSSOU
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,107 @@
1
+ # amistad #
2
+
3
+ Amistad adds friendships management into a rails 3.0 application.
4
+
5
+ ## Installation ##
6
+
7
+ Just run the following command :
8
+
9
+ gem install amistad
10
+
11
+ Then in your gem file add the following line :
12
+
13
+ gem 'amistad'
14
+
15
+ ## Usage ##
16
+
17
+ First generate a friendship model :
18
+
19
+ rails generate friendship
20
+
21
+ This commands create a new model called __friendship__ in *'app/models'* :
22
+
23
+ class Friendship < ActiveRecord::Base
24
+ acts_as_friendship
25
+ end
26
+
27
+ It also creates a new migration for the friendship model so don't forget to migrate your database :
28
+
29
+ db:migrate
30
+
31
+ Then activate __amistad__ in your user model :
32
+
33
+ class User < ActiveRecord::Base
34
+ acts_as_friend
35
+ end
36
+
37
+ ## Friendships management ##
38
+
39
+ ### Creating friendships ###
40
+ To create a new friendship with another user use the method called __invite()__ :
41
+
42
+ @john.invite @jane
43
+ @peter.invite @john
44
+ @peter.invite @jane
45
+ @victoria.invite @john
46
+
47
+ The __invite()__ method return *true* if the friendship successfully created, otherwise it returns *false*. The friendships remain in pending state until they are approved by the user requested. To approve the friendship created above use the method called __approve()__ :
48
+
49
+ @jane.approve @john
50
+ @john.approve @peter
51
+ @jane.approve @peter
52
+
53
+ As __invite()__, __approve()__ return *true* if the friendship was successfuly approved or *false* if not.
54
+
55
+ ### Listing friends ###
56
+
57
+ There are two types of friends in __amistad__ :
58
+
59
+ - the friends who were invited by the user
60
+ - the friends who invited the user
61
+
62
+ To get the friend who where invited by __@john__, use the __invited()__ method :
63
+
64
+ @john.invited #=> [@jane]
65
+
66
+ To get the friends who invited __@john__, use the __invited_by()__ method :
67
+
68
+ @john.invited_by #=> [@peter]
69
+
70
+ To get all the friends of __@john__ (those he invited and those who invited him) :
71
+
72
+ @john.friends #=> [@jane, @peter]
73
+
74
+ To get the pending friendships use :
75
+
76
+ @victoria.pending_invited #=> [@john]
77
+ @john.pending_invited_by #=> [@victoria]
78
+
79
+ It is also possible to check if two users are friends :
80
+
81
+ @john.is_friend_with? @jane #=> true
82
+ @victoria.is_friend_with? @john #=> false
83
+
84
+ You can also find the friends that two users have in common :
85
+
86
+ @john.common_friend_with(@peter) #=> [@jane]
87
+
88
+ ### Removing friendships ###
89
+
90
+ The __remove()__ method allow a user to remove its friendships :
91
+
92
+ @john.remove @jane
93
+ @john.remove @peter
94
+ @john.remove @victoria
95
+
96
+ ## Note on Patches/Pull Requests ##
97
+
98
+ * Fork the project.
99
+ * Make your feature addition or bug fix.
100
+ * Add tests for it. This is important so I don't break it in a future version unintentionally.
101
+ *
102
+ * Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
103
+ * Send me a pull request. Bonus points for topic branches.
104
+
105
+ ## Copyright ##
106
+
107
+ Copyright © 2010 Rawane ZOSSOU. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,27 @@
1
+ require 'rake'
2
+
3
+ begin
4
+ require 'jeweler'
5
+ Jeweler::Tasks.new do |gemspec|
6
+ gemspec.name = "amistad"
7
+ gemspec.summary = "Adds friendships management into a rails 3.0 application"
8
+ gemspec.description = "Extends your user model with friendships management methods"
9
+ gemspec.email = "dev@raw1z.fr"
10
+ gemspec.homepage = "http://github.com/raw1z/amistad"
11
+ gemspec.authors = ["Rawane ZOSSOU"]
12
+ gemspec.files = FileList["[A-Z]*", "{lib}/**/*"]
13
+ end
14
+ rescue LoadError
15
+ puts "Jeweler not available. Install it with: gem install jeweler"
16
+ end
17
+
18
+ begin
19
+ require 'spec/rake/spectask'
20
+ desc "Run the tests"
21
+ Spec::Rake::SpecTask.new(:spec) do |t|
22
+ t.spec_opts = ["--format", "specdoc", "--color"]
23
+ t.spec_files = Dir.glob('spec/**/*_spec.rb')
24
+ end
25
+ rescue LoadError
26
+ puts "Rspec not available. Install it with: gem install rspec"
27
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
data/lib/amistad.rb ADDED
@@ -0,0 +1,2 @@
1
+ require File.join(File.dirname(__FILE__), 'amistad/friend_model')
2
+ require File.join(File.dirname(__FILE__), 'amistad/friendship_model')
@@ -0,0 +1,92 @@
1
+ module Amistad
2
+ module FriendModel
3
+ def self.included(base)
4
+ base.extend ClassMethods
5
+ end
6
+
7
+ module ClassMethods
8
+ def acts_as_friend
9
+ has_many :friendships
10
+
11
+ has_many :pending_invited,
12
+ :through => :friendships,
13
+ :source => :friend,
14
+ :conditions => { :'friendships.pending' => true }
15
+
16
+ has_many :invited,
17
+ :through => :friendships,
18
+ :source => :friend,
19
+ :conditions => { :'friendships.pending' => false }
20
+
21
+ has_many :inverse_friendships, :class_name => "Friendship", :foreign_key => "friend_id"
22
+
23
+ has_many :pending_invited_by,
24
+ :through => :inverse_friendships,
25
+ :source => :user,
26
+ :conditions => {:'friendships.pending' => true}
27
+
28
+ has_many :invited_by,
29
+ :through => :inverse_friendships,
30
+ :source => :user,
31
+ :conditions => {:'friendships.pending' => false}
32
+
33
+ class_eval <<-EOV
34
+ include Amistad::FriendModel::InstanceMethods
35
+ EOV
36
+ end
37
+ end
38
+
39
+ module InstanceMethods
40
+ def invite(user)
41
+ return false if user == self
42
+
43
+ friendship = find_any_friendship_with(user)
44
+ return false if not friendship.nil?
45
+
46
+ friendship = Friendship.new(:user_id => self.id, :friend_id => user.id)
47
+ friendship.save
48
+ end
49
+
50
+ def approve(user)
51
+ friendship = find_any_friendship_with(user)
52
+ return false if friendship.nil?
53
+ friendship.pending = false
54
+ friendship.save
55
+ end
56
+
57
+ def friends
58
+ self.invited(true) + self.invited_by(true)
59
+ end
60
+
61
+ def remove(user)
62
+ friendship = find_any_friendship_with(user)
63
+ return false if friendship.nil?
64
+ friendship.destroy
65
+ friendship.destroyed?
66
+ end
67
+
68
+ def is_friend_with?(user)
69
+ friends.include?(user)
70
+ end
71
+
72
+ def common_friends_with(user)
73
+ self.friends & user.friends
74
+ end
75
+
76
+ private
77
+
78
+ def find_any_friendship_with(user)
79
+ friendship = Friendship.where(:user_id => self.id, :friend_id => user.id).first
80
+ if friendship.nil?
81
+ friendship = Friendship.where(:user_id => user.id, :friend_id => self.id).first
82
+ end
83
+ friendship
84
+ end
85
+
86
+ end
87
+ end
88
+ end
89
+
90
+ class ActiveRecord::Base
91
+ include Amistad::FriendModel
92
+ end
@@ -0,0 +1,32 @@
1
+ module Amistad
2
+ module FriendshipModel
3
+
4
+ def self.included(base)
5
+ base.extend ClassMethods
6
+ end
7
+
8
+ module ClassMethods
9
+ def acts_as_friendship
10
+ belongs_to :user
11
+ belongs_to :friend, :class_name => "User", :foreign_key => "friend_id"
12
+
13
+ validates_presence_of :user_id, :friend_id
14
+
15
+ class_eval <<-EOV
16
+ include Amistad::FriendshipModel::InstanceMethods
17
+ EOV
18
+ end
19
+ end
20
+
21
+ module InstanceMethods
22
+ def pending?
23
+ self.pending
24
+ end
25
+ end
26
+
27
+ end
28
+ end
29
+
30
+ class ActiveRecord::Base
31
+ include Amistad::FriendshipModel
32
+ end
@@ -0,0 +1,21 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/migration'
3
+
4
+ class FriendshipGenerator < Rails::Generators::Base
5
+ include Rails::Generators::Migration
6
+
7
+ def self.next_migration_number
8
+ now = DateTime.now
9
+ "#{now.year}#{'%02d' % now.month}#{'%02d' % now.day}#{'%02d' % now.hour}#{'%02d' % now.minute}#{'%02d' % now.second}"
10
+ end
11
+
12
+ def self.source_root
13
+ @source_root ||= File.expand_path(File.join(File.dirname(__FILE__), 'templates'))
14
+ end
15
+
16
+ desc "This generator creates a friendship model and its migration file"
17
+ def create_friendship_model_files
18
+ template 'friendship.rb', 'app/models/friendship.rb'
19
+ template 'create_friendships.rb', "db/migrate/#{self.class.next_migration_number}_create_friendships.rb"
20
+ end
21
+ end
@@ -0,0 +1,13 @@
1
+ class CreateFriendships < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :friendships do |t|
4
+ t.integer :user_id
5
+ t.integer :friend_id
6
+ t.boolean :pending, :default => true
7
+ end
8
+ end
9
+
10
+ def self.down
11
+ drop_table :friendships
12
+ end
13
+ end
@@ -0,0 +1,3 @@
1
+ class Friendship < ActiveRecord::Base
2
+ acts_as_friendship
3
+ end
@@ -0,0 +1,149 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Amistad::FriendModel do
4
+
5
+ before(:all) do
6
+ %w(John Jane david James Peter Mary Victoria Elisabeth).each do |name|
7
+ eval "@#{name.downcase} = User.create(:name => '#{name}')"
8
+ end
9
+ end
10
+
11
+ context "when creating friendships" do
12
+ before(:each) do
13
+ Friendship.delete_all
14
+ end
15
+
16
+ it "requests frienships with other users" do
17
+ @john.invite(@jane).should == true
18
+ @victoria.invite(@john).should == true
19
+ end
20
+
21
+ it "approves frienships requested by other users" do
22
+ @john.invite(@jane).should == true
23
+ @victoria.invite(@john).should == true
24
+
25
+ @jane.approve(@john).should == true
26
+ @john.approve(@victoria).should == true
27
+ end
28
+
29
+ it "could not create a new friendship with a user which is already a friend" do
30
+ @john.invite(@jane).should == true
31
+ @john.invite(@jane).should == false
32
+ @jane.invite(@john).should == false
33
+ end
34
+
35
+ it "could not create a friendship with himself" do
36
+ @john.invite(@john).should == false
37
+ end
38
+
39
+ it "could not approuve a non-existent friendship" do
40
+ @peter.approve(@john).should == false
41
+ end
42
+ end
43
+
44
+ context "when listing friendships" do
45
+ before(:each) do
46
+ Friendship.delete_all
47
+ @john.invite(@jane).should == true
48
+ @john.invite(@james).should == true
49
+
50
+ @peter.invite(@john).should == true
51
+ @mary.invite(@john).should == true
52
+
53
+ @james.approve(@john).should == true
54
+ @john.approve(@mary).should == true
55
+ end
56
+
57
+ it "lists all the friends" do
58
+ @john.friends.count.should == 2
59
+ @john.friends.should include(@james)
60
+ @john.friends.should include(@mary)
61
+ end
62
+
63
+ it "lists the friends who invited him" do
64
+ @john.invited_by.should include(@mary)
65
+ @john.invited_by.should_not include(@peter)
66
+ end
67
+
68
+ it "lists the friends who was invited by him" do
69
+ @john.invited.should include(@james)
70
+ @john.invited.should_not include(@jane)
71
+ end
72
+
73
+ it "lists the pending friends who invited him" do
74
+ @john.pending_invited_by.should_not include(@mary)
75
+ @john.pending_invited_by.should include(@peter)
76
+ end
77
+
78
+ it "lists the pending friends who was invited by him" do
79
+ @john.pending_invited.should_not include(@james)
80
+ @john.pending_invited.should include(@jane)
81
+ end
82
+
83
+ it "checks if a user is a friend" do
84
+ @john.is_friend_with?(@james).should == true
85
+ @john.is_friend_with?(@mary).should == true
86
+
87
+ @john.is_friend_with?(@jane).should == false
88
+ @john.is_friend_with?(@peter).should == false
89
+ end
90
+
91
+ it "lists the friends he has in common with another user" do
92
+ @james.common_friends_with(@mary).count.should == 1
93
+ @james.common_friends_with(@mary).should include(@john)
94
+ end
95
+ end
96
+
97
+ context "when removing friendships" do
98
+ before(:each) do
99
+ Friendship.delete_all
100
+ @victoria.invite(@mary).should == true
101
+ @victoria.invite(@john).should == true
102
+ @victoria.invite(@elisabeth).should == true
103
+
104
+ @james.invite(@victoria).should == true
105
+ @peter.invite(@victoria).should == true
106
+ @jane.invite(@victoria).should == true
107
+
108
+ @mary.approve(@victoria).should == true
109
+ @john.approve(@victoria).should == true
110
+ @victoria.approve(@james).should == true
111
+ @victoria.approve(@jane).should == true
112
+ end
113
+
114
+ it "removes the friends invited by him" do
115
+ @victoria.remove(@mary).should == true
116
+
117
+ @victoria.invited.count.should == 1
118
+ @victoria.invited.should include(@john)
119
+ @victoria.friends.should_not include(@mary)
120
+
121
+ @mary.invited_by.should_not include(@victoria)
122
+ @mary.friends.should_not include(@victoria)
123
+ end
124
+
125
+ it "removes the friends who invited him" do
126
+ @victoria.remove(@james).should == true
127
+
128
+ @victoria.invited_by.count.should == 1
129
+ @victoria.invited_by.should include(@jane)
130
+ @victoria.friends.should_not include(@james)
131
+
132
+ @james.invited.should_not include(@victoria)
133
+ @james.friends.should_not include(@victoria)
134
+ end
135
+
136
+ it "removes the pending friends invited by him" do
137
+ @victoria.remove(@elisabeth).should == true
138
+ @victoria.pending_invited.count.should == 0
139
+ @elisabeth.pending_invited_by.should_not include(@victoria)
140
+ end
141
+
142
+ it "removes the pending friends who invited him" do
143
+ @victoria.remove(@peter).should == true
144
+ @victoria.pending_invited_by.count.should == 0
145
+ @peter.pending_invited.should_not include(@victoria)
146
+ end
147
+ end
148
+
149
+ end
@@ -0,0 +1,21 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Amistad::FriendshipModel do
4
+
5
+ before(:each) do
6
+ Friendship.delete_all
7
+ end
8
+
9
+ it "validates presence of the user's id and the friend's id" do
10
+ friendship = Friendship.new
11
+ friendship.save.should == false
12
+
13
+ friendship = Friendship.new(:user_id => 1, :friend_id => 2)
14
+ friendship.save.should == true
15
+ end
16
+
17
+ it "is in pending state when created" do
18
+ friendship = Friendship.create(:user_id => 1, :friend_id => 2)
19
+ friendship.pending?.should == true
20
+ end
21
+ end
@@ -0,0 +1,32 @@
1
+ require 'rubygems'
2
+ require 'active_record'
3
+ require 'logger'
4
+
5
+ require File.join(File.dirname(__FILE__), '../lib/amistad')
6
+
7
+ ActiveRecord::Base.establish_connection(
8
+ :adapter => "sqlite3",
9
+ :database => "tmp/amistad.sqlite3.db"
10
+ )
11
+
12
+ ActiveRecord::Schema.define do
13
+ create_table :users, :force => true do |t|
14
+ t.string :name, :null => false
15
+ end
16
+
17
+ create_table :friendships, :force => true do |t|
18
+ t.integer :user_id
19
+ t.integer :friend_id
20
+ t.boolean :pending, :default => true
21
+ end
22
+ end
23
+
24
+ ActiveRecord::Base.logger = Logger.new(File.open('tmp/database.log', 'a'))
25
+
26
+ class User < ActiveRecord::Base
27
+ acts_as_friend
28
+ end
29
+
30
+ class Friendship < ActiveRecord::Base
31
+ acts_as_friendship
32
+ end
metadata ADDED
@@ -0,0 +1,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: amistad
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Rawane ZOSSOU
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-04-13 00:00:00 +02:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: Extends your user model with friendships management methods
22
+ email: dev@raw1z.fr
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files:
28
+ - README.markdown
29
+ files:
30
+ - CHANGELOG
31
+ - LICENCE
32
+ - README.markdown
33
+ - Rakefile
34
+ - VERSION
35
+ - lib/amistad.rb
36
+ - lib/amistad/friend_model.rb
37
+ - lib/amistad/friendship_model.rb
38
+ - lib/generators/friendship/friendship_generator.rb
39
+ - lib/generators/friendship/templates/create_friendships.rb
40
+ - lib/generators/friendship/templates/friendship.rb
41
+ has_rdoc: true
42
+ homepage: http://github.com/raw1z/amistad
43
+ licenses: []
44
+
45
+ post_install_message:
46
+ rdoc_options:
47
+ - --charset=UTF-8
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ segments:
55
+ - 0
56
+ version: "0"
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ segments:
62
+ - 0
63
+ version: "0"
64
+ requirements: []
65
+
66
+ rubyforge_project:
67
+ rubygems_version: 1.3.6
68
+ signing_key:
69
+ specification_version: 3
70
+ summary: Adds friendships management into a rails 3.0 application
71
+ test_files:
72
+ - spec/friend_model_spec.rb
73
+ - spec/friendship_model_spec.rb
74
+ - spec/spec_helper.rb