amistad 0.7.5 → 0.9.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.
Files changed (41) hide show
  1. data/.gitignore +4 -1
  2. data/Gemfile.lock +56 -39
  3. data/README.markdown +29 -126
  4. data/Rakefile +35 -1
  5. data/amistad.gemspec +8 -3
  6. data/lib/amistad.rb +14 -9
  7. data/lib/amistad/active_record_friend_model.rb +157 -0
  8. data/lib/amistad/active_record_friendship_model.rb +52 -0
  9. data/lib/amistad/config.rb +21 -0
  10. data/lib/amistad/friend_model.rb +10 -4
  11. data/lib/amistad/friendship_model.rb +2 -2
  12. data/lib/amistad/friendships.rb +19 -0
  13. data/lib/amistad/mongo_friend_model.rb +168 -0
  14. data/lib/amistad/mongo_mapper_friend_model.rb +58 -0
  15. data/lib/amistad/mongoid_friend_model.rb +58 -0
  16. data/lib/amistad/version.rb +1 -1
  17. data/lib/generators/amistad/install/install_generator.rb +0 -1
  18. data/lib/generators/amistad/install/templates/create_friendships.rb +2 -2
  19. data/spec/activerecord/activerecord_spec_helper.rb +11 -24
  20. data/spec/activerecord/friend_custom_model_spec.rb +17 -0
  21. data/spec/activerecord/friend_spec.rb +16 -0
  22. data/spec/activerecord/friendship_spec.rb +13 -0
  23. data/spec/activerecord/friendship_with_custom_friend_model_spec.rb +18 -0
  24. data/spec/mongo_mapper/friend_custom_model_spec.rb +27 -0
  25. data/spec/mongo_mapper/friend_spec.rb +17 -0
  26. data/spec/mongo_mapper/mongo_mapper_spec_helper.rb +9 -0
  27. data/spec/mongoid/friend_custom_model_spec.rb +27 -0
  28. data/spec/mongoid/friend_spec.rb +17 -0
  29. data/spec/mongoid/mongoid_spec_helper.rb +8 -7
  30. data/spec/spec_helper.rb +33 -4
  31. data/spec/{activerecord/activerecord_friendship_model_spec.rb → support/activerecord/friendship_examples.rb} +11 -23
  32. data/spec/support/activerecord/schema.rb +40 -0
  33. data/spec/support/friend_examples.rb +2 -12
  34. data/spec/support/parameterized_models.rb +19 -0
  35. metadata +177 -36
  36. data/lib/amistad/active_record/friend_model.rb +0 -146
  37. data/lib/amistad/active_record/friendship_model.rb +0 -50
  38. data/lib/amistad/mongoid/friend_model.rb +0 -195
  39. data/lib/generators/amistad/install/templates/friendship.rb +0 -3
  40. data/spec/activerecord/activerecord_friend_model_spec.rb +0 -13
  41. data/spec/mongoid/mongoid_friend_model_spec.rb +0 -47
@@ -0,0 +1,52 @@
1
+ module Amistad
2
+ module ActiveRecordFriendshipModel
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ belongs_to :friendable,
7
+ :class_name => Amistad.friend_model,
8
+ :foreign_key => "friendable_id"
9
+
10
+ belongs_to :friend,
11
+ :class_name => Amistad.friend_model,
12
+ :foreign_key => "friend_id"
13
+
14
+ belongs_to :blocker,
15
+ :class_name => Amistad.friend_model,
16
+ :foreign_key => "blocker_id"
17
+
18
+ validates_presence_of :friendable_id, :friend_id
19
+ validates_uniqueness_of :friend_id, :scope => :friendable_id
20
+ end
21
+
22
+ # returns true if a friendship has been approved, else false
23
+ def approved?
24
+ !self.pending
25
+ end
26
+
27
+ # returns true if a friendship has not been approved, else false
28
+ def pending?
29
+ self.pending
30
+ end
31
+
32
+ # returns true if a friendship has been blocked, else false
33
+ def blocked?
34
+ self.blocker_id.present?
35
+ end
36
+
37
+ # returns true if a friendship has not beed blocked, else false
38
+ def active?
39
+ self.blocker_id.nil?
40
+ end
41
+
42
+ # returns true if a friendship can be blocked by given friendable
43
+ def can_block?(friendable)
44
+ active? && (approved? || (pending? && self.friend_id == friendable.id && friendable.class.to_s == Amistad.friend_model))
45
+ end
46
+
47
+ # returns true if a friendship can be unblocked by given friendable
48
+ def can_unblock?(friendable)
49
+ blocked? && self.blocker_id == friendable.id && friendable.class.to_s == Amistad.friend_model
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,21 @@
1
+ module Amistad
2
+ class << self
3
+ attr_accessor :friend_model
4
+
5
+ def configure
6
+ yield self
7
+ end
8
+
9
+ def friend_model
10
+ @friend_model || 'User'
11
+ end
12
+
13
+ def friendship_model
14
+ "#{self.friend_model}Friendship"
15
+ end
16
+
17
+ def friendship_class
18
+ Amistad::Friendships.const_get(self.friendship_model)
19
+ end
20
+ end
21
+ end
@@ -3,15 +3,21 @@ module Amistad
3
3
  def self.included(receiver)
4
4
  if receiver.ancestors.map(&:to_s).include?("ActiveRecord::Base")
5
5
  receiver.class_exec do
6
- include Amistad::ActiveRecord::FriendModel
6
+ include Amistad::ActiveRecordFriendModel
7
7
  end
8
8
  elsif receiver.ancestors.map(&:to_s).include?("Mongoid::Document")
9
9
  receiver.class_exec do
10
- include Amistad::Mongoid::FriendModel
10
+ include Amistad::MongoidFriendModel
11
+ include Amistad::MongoFriendModel
12
+ end
13
+ elsif receiver.ancestors.map(&:to_s).include?("MongoMapper::Document")
14
+ receiver.class_exec do
15
+ include Amistad::MongoMapperFriendModel
16
+ include Amistad::MongoFriendModel
11
17
  end
12
18
  else
13
- raise "Amistad only supports ActiveRecord and Mongoid"
19
+ raise "Amistad only supports ActiveRecord, Mongoid and MongoMapper"
14
20
  end
15
21
  end
16
22
  end
17
- end
23
+ end
@@ -3,11 +3,11 @@ module Amistad
3
3
  def self.included(receiver)
4
4
  if receiver.ancestors.map(&:to_s).include?("ActiveRecord::Base")
5
5
  receiver.class_exec do
6
- include Amistad::ActiveRecord::FriendshipModel
6
+ include Amistad::ActiveRecordFriendshipModel
7
7
  end
8
8
  else
9
9
  raise "Amistad only supports ActiveRecord and Mongoid"
10
10
  end
11
11
  end
12
12
  end
13
- end
13
+ end
@@ -0,0 +1,19 @@
1
+ module Amistad
2
+ module Friendships
3
+ if Object.const_defined? :ActiveRecord
4
+ const_set Amistad.friendship_model, Class.new(ActiveRecord::Base)
5
+ const_get(Amistad.friendship_model.to_sym).class_exec do
6
+ include Amistad::FriendshipModel
7
+ self.table_name = 'friendships'
8
+ end
9
+ elsif Object.const_defined? :Mongoid
10
+ Friendship = Class.new
11
+ Friendship.class_exec do
12
+ include Mongoid::Document
13
+ include Amistad::FriendshipModel
14
+ end
15
+ else
16
+ raise "Amistad only supports ActiveRecord and Mongoid"
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,168 @@
1
+ module Amistad
2
+ module MongoFriendModel
3
+ # suggest a user to become a friend. If the operation succeeds, the method returns true, else false
4
+ def invite(user)
5
+ return false if friendshiped_with?(user) or user == self or blocked?(user)
6
+ pending_friend_ids << user.id
7
+ user.pending_inverse_friend_ids << self.id
8
+ self.save && user.save
9
+ end
10
+
11
+ # approve a friendship invitation. If the operation succeeds, the method returns true, else false
12
+ def approve(user)
13
+ return false unless pending_inverse_friend_ids.include?(user.id) && user.pending_friend_ids.include?(self.id)
14
+ pending_inverse_friend_ids.delete(user.id)
15
+ user.pending_friend_ids.delete(self.id)
16
+ inverse_friend_ids << user.id
17
+ user.friend_ids << self.id
18
+ self.save && user.save
19
+ end
20
+
21
+ # returns the list of approved friends
22
+ def friends
23
+ self.invited + self.invited_by
24
+ end
25
+
26
+ # total # of invited and invited_by without association loading
27
+ def total_friends
28
+ (friend_ids + inverse_friend_ids).count
29
+ end
30
+
31
+ # return the list of invited friends
32
+ def invited
33
+ self.class.find(friend_ids)
34
+ end
35
+
36
+ # return the list of friends who invited
37
+ def invited_by
38
+ self.class.find(inverse_friend_ids)
39
+ end
40
+
41
+ # return the list of pending invited friends
42
+ def pending_invited
43
+ self.class.find(pending_friend_ids)
44
+ end
45
+
46
+ # return the list of pending friends who invited
47
+ def pending_invited_by
48
+ self.class.find(pending_inverse_friend_ids)
49
+ end
50
+
51
+ # return the list of the ones among its friends which are also friend with the given use
52
+ def common_friends_with(user)
53
+ self.friends & user.friends
54
+ end
55
+
56
+ # checks if a user is a friend
57
+ def friend_with?(user)
58
+ return false if user == self
59
+ (friend_ids + inverse_friend_ids).include?(user.id)
60
+ end
61
+
62
+ # checks if a current user is connected to given user
63
+ def connected_with?(user)
64
+ friendshiped_with?(user)
65
+ end
66
+
67
+ # checks if a current user received invitation from given user
68
+ def invited_by?(user)
69
+ user.friend_ids.include?(self.id) or user.pending_friend_ids.include?(self.id)
70
+ end
71
+
72
+ # checks if a current user invited given user
73
+ def invited?(user)
74
+ self.friend_ids.include?(user.id) or self.pending_friend_ids.include?(user.id)
75
+ end
76
+
77
+ # deletes a friendship
78
+ def remove_friendship(user)
79
+ friend_ids.delete(user.id)
80
+ user.inverse_friend_ids.delete(self.id)
81
+ inverse_friend_ids.delete(user.id)
82
+ user.friend_ids.delete(self.id)
83
+ pending_friend_ids.delete(user.id)
84
+ user.pending_inverse_friend_ids.delete(self.id)
85
+ pending_inverse_friend_ids.delete(user.id)
86
+ user.pending_friend_ids.delete(self.id)
87
+ self.save && user.save
88
+ end
89
+
90
+ # blocks a friendship
91
+ def block(user)
92
+ if inverse_friend_ids.include?(user.id)
93
+ inverse_friend_ids.delete(user.id)
94
+ user.friend_ids.delete(self.id)
95
+ blocked_inverse_friend_ids << user.id
96
+ elsif pending_inverse_friend_ids.include?(user.id)
97
+ pending_inverse_friend_ids.delete(user.id)
98
+ user.pending_friend_ids.delete(self.id)
99
+ blocked_pending_inverse_friend_ids << user.id
100
+ elsif friend_ids.include?(user.id)
101
+ friend_ids.delete(user.id)
102
+ user.inverse_friend_ids.delete(user.id)
103
+ blocked_friend_ids << user.id
104
+ else
105
+ return false
106
+ end
107
+
108
+ self.save
109
+ end
110
+
111
+ # unblocks a friendship
112
+ def unblock(user)
113
+ if blocked_inverse_friend_ids.include?(user.id)
114
+ blocked_inverse_friend_ids.delete(user.id)
115
+ user.blocked_friend_ids.delete(self.id)
116
+ inverse_friend_ids << user.id
117
+ user.friend_ids << self.id
118
+ elsif blocked_pending_inverse_friend_ids.include?(user.id)
119
+ blocked_pending_inverse_friend_ids.delete(user.id)
120
+ pending_inverse_friend_ids << user.id
121
+ user.pending_friend_ids << self.id
122
+ elsif blocked_friend_ids.include?(user.id)
123
+ blocked_friend_ids.delete(user.id)
124
+ user.blocked_inverse_friend_ids.delete(self.id)
125
+ friend_ids << user.id
126
+ user.inverse_friend_ids << self.id
127
+ else
128
+ return false
129
+ end
130
+
131
+ self.save && user.save
132
+ end
133
+
134
+ # returns the list of blocked friends
135
+ def blocked
136
+ blocked_ids = blocked_friend_ids + blocked_inverse_friend_ids + blocked_pending_inverse_friend_ids
137
+ self.class.find(blocked_ids)
138
+ end
139
+
140
+ # total # of blockades and blockedes_by without association loading
141
+ def total_blocked
142
+ (blocked_friend_ids + blocked_inverse_friend_ids + blocked_pending_inverse_friend_ids).count
143
+ end
144
+
145
+ # checks if a user is blocked
146
+ def blocked?(user)
147
+ (blocked_friend_ids + blocked_inverse_friend_ids + blocked_pending_inverse_friend_ids).include?(user.id) or user.blocked_pending_inverse_friend_ids.include?(self.id)
148
+ end
149
+
150
+ # check if any friendship exists with another user
151
+ def friendshiped_with?(user)
152
+ (friend_ids + inverse_friend_ids + pending_friend_ids + pending_inverse_friend_ids + blocked_friend_ids).include?(user.id)
153
+ end
154
+
155
+ # deletes all the friendships
156
+ def delete_all_friendships
157
+ friend_ids.clear
158
+ inverse_friend_ids.clear
159
+ pending_friend_ids.clear
160
+ pending_inverse_friend_ids.clear
161
+ blocked_friend_ids.clear
162
+ blocked_inverse_friend_ids.clear
163
+ blocked_pending_friend_ids.clear
164
+ blocked_pending_inverse_friend_ids.clear
165
+ self.save
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,58 @@
1
+ module Amistad
2
+ module MongoMapperFriendModel
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ key :friend_ids,
7
+ Array,
8
+ :default => []
9
+
10
+ key :inverse_friend_ids,
11
+ Array,
12
+ :default => []
13
+
14
+ key :pending_friend_ids,
15
+ Array,
16
+ :default => []
17
+
18
+ key :pending_inverse_friend_ids,
19
+ Array,
20
+ :default => []
21
+
22
+ key :blocked_friend_ids,
23
+ Array,
24
+ :default => []
25
+
26
+ key :blocked_inverse_friend_ids,
27
+ Array,
28
+ :default => []
29
+
30
+ key :blocked_pending_friend_ids,
31
+ Array,
32
+ :default => []
33
+
34
+ key :blocked_pending_inverse_friend_ids,
35
+ Array,
36
+ :default => []
37
+
38
+ attr_accessible(
39
+ :friend_ids,
40
+ :inverse_friend_ids,
41
+ :pending_friend_ids,
42
+ :pending_inverse_friend_ids,
43
+ :blocked_friend_ids,
44
+ :blocked_inverse_friend_ids,
45
+ :blocked_pending_friend_ids,
46
+ :blocked_pending_inverse_friend_ids,
47
+ )
48
+
49
+ %w(friend_ids inverse_friend_ids pending_friend_ids pending_inverse_friend_ids blocked_friend_ids blocked_inverse_friend_ids blocked_pending_friend_ids blocked_pending_inverse_friend_ids).each do |attribute|
50
+ define_method(attribute.to_sym) do
51
+ value = read_attribute(attribute)
52
+ write_attribute(attribute, value = []) if value.nil?
53
+ value
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,58 @@
1
+ module Amistad
2
+ module MongoidFriendModel
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ field :friend_ids,
7
+ :type => Array,
8
+ :default => []
9
+
10
+ field :inverse_friend_ids,
11
+ :type => Array,
12
+ :default => []
13
+
14
+ field :pending_friend_ids,
15
+ :type => Array,
16
+ :default => []
17
+
18
+ field :pending_inverse_friend_ids,
19
+ :type => Array,
20
+ :default => []
21
+
22
+ field :blocked_friend_ids,
23
+ :type => Array,
24
+ :default => []
25
+
26
+ field :blocked_inverse_friend_ids,
27
+ :type => Array,
28
+ :default => []
29
+
30
+ field :blocked_pending_friend_ids,
31
+ :type => Array,
32
+ :default => []
33
+
34
+ field :blocked_pending_inverse_friend_ids,
35
+ :type => Array,
36
+ :default => []
37
+
38
+ attr_accessible(
39
+ :friend_ids,
40
+ :inverse_friend_ids,
41
+ :pending_friend_ids,
42
+ :pending_inverse_friend_ids,
43
+ :blocked_friend_ids,
44
+ :blocked_inverse_friend_ids,
45
+ :blocked_pending_friend_ids,
46
+ :blocked_pending_inverse_friend_ids,
47
+ )
48
+
49
+ %w(friend_ids inverse_friend_ids pending_friend_ids pending_inverse_friend_ids blocked_friend_ids blocked_inverse_friend_ids blocked_pending_friend_ids blocked_pending_inverse_friend_ids).each do |attribute|
50
+ define_method(attribute.to_sym) do
51
+ value = read_attribute(attribute)
52
+ write_attribute(attribute, value = []) if value.nil?
53
+ value
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -1,3 +1,3 @@
1
1
  module Amistad
2
- VERSION = "0.7.5"
2
+ VERSION = "0.9.0"
3
3
  end
@@ -16,7 +16,6 @@ module Amistad
16
16
 
17
17
  desc "This generator creates a friendship model and its migration file"
18
18
  def create_friendship_model_files
19
- template 'friendship.rb', 'app/models/friendship.rb'
20
19
  template 'create_friendships.rb', "db/migrate/#{self.class.next_migration_number}_create_friendships.rb"
21
20
  end
22
21
  end
@@ -1,13 +1,13 @@
1
1
  class CreateFriendships < ActiveRecord::Migration
2
2
  def self.up
3
3
  create_table :friendships do |t|
4
- t.integer :user_id
4
+ t.integer :friendable_id
5
5
  t.integer :friend_id
6
6
  t.integer :blocker_id
7
7
  t.boolean :pending, :default => true
8
8
  end
9
9
 
10
- add_index :friendships, [:user_id, :friend_id], :unique => true
10
+ add_index :friendships, [:friendable_id, :friend_id], :unique => true
11
11
  end
12
12
 
13
13
  def self.down