amico 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -4,3 +4,4 @@ Gemfile.lock
4
4
  pkg/*
5
5
  .yardoc
6
6
  doc
7
+ *.rdb
@@ -1,3 +1,7 @@
1
+ # 1.2.0
2
+
3
+ * Added pending to relationships
4
+
1
5
  # 1.1.0
2
6
 
3
7
  * Added blocking to relationships
data/README.md CHANGED
@@ -27,6 +27,8 @@ Amico.configure do |configuration|
27
27
  configuration.followers_key = 'followers'
28
28
  configuration.blocked_key = 'blocked'
29
29
  configuration.reciprocated_key = 'reciprocated'
30
+ configuration.pending_key = 'pending'
31
+ configuration.pending_follow = false
30
32
  configuration.page_size = 25
31
33
  end
32
34
  ```
@@ -44,6 +46,8 @@ Amico.configure do |configuration|
44
46
  configuration.followers_key = 'followers'
45
47
  configuration.blocked_key = 'blocked'
46
48
  configuration.reciprocated_key = 'reciprocated'
49
+ configuration.pending_key = 'pending'
50
+ configuration.pending_follow = false
47
51
  configuration.page_size = 25
48
52
  end
49
53
 
@@ -111,10 +115,98 @@ Amico.reciprocated(1)
111
115
  => ["11"]
112
116
  ```
113
117
 
118
+ Use amico (with pending relationships for follow):
119
+
120
+ ```ruby
121
+ require 'amico'
122
+ => true
123
+
124
+ Amico.configure do |configuration|
125
+ configuration.redis = Redis.new
126
+ configuration.namespace = 'amico'
127
+ configuration.following_key = 'following'
128
+ configuration.followers_key = 'followers'
129
+ configuration.blocked_key = 'blocked'
130
+ configuration.reciprocated_key = 'reciprocated'
131
+ configuration.pending_key = 'pending'
132
+ configuration.pending_follow = true
133
+ configuration.page_size = 25
134
+ end
135
+
136
+ Amico.follow(1, 11)
137
+ => true
138
+
139
+ Amico.follow(11, 1)
140
+ => true
141
+
142
+ Amico.pending?(1, 11)
143
+ => true
144
+
145
+ Amico.pending?(11, 1)
146
+ => true
147
+
148
+ Amico.accept(1, 11)
149
+ => nil
150
+
151
+ Amico.pending?(1, 11)
152
+ => false
153
+
154
+ Amico.pending?(11, 1)
155
+ => true
156
+
157
+ Amico.following?(1, 11)
158
+ => true
159
+
160
+ Amico.following?(11, 1)
161
+ => false
162
+
163
+ Amico.follower?(11, 1)
164
+ => true
165
+
166
+ Amico.follower?(1, 11)
167
+ => false
168
+
169
+ Amico.accept(11, 1)
170
+ => [1, 1]
171
+
172
+ Amico.pending?(1, 11)
173
+ => false
174
+
175
+ Amico.pending?(11, 1)
176
+ => false
177
+
178
+ Amico.following?(1, 11)
179
+ => true
180
+
181
+ Amico.following?(11, 1)
182
+ => true
183
+
184
+ Amico.follower?(11, 1)
185
+ => true
186
+
187
+ Amico.follower?(1, 11)
188
+ => true
189
+
190
+ Amico.reciprocated?(1, 11)
191
+ => true
192
+ ```
193
+
114
194
  ## Documentation
115
195
 
116
196
  The source for the [relationships module](https://github.com/agoragames/amico/blob/master/lib/amico/relationships.rb) is well-documented. There are some
117
- simple examples in the method documentation. You can also refer to the [online documentation](http://rubydoc.info/gems/amico/).
197
+ simple examples in the method documentation. You can also refer to the [online documentation](http://rubydoc.info/github/agoragames/amico/master/frames).
198
+
199
+ ## FAQ?
200
+
201
+ ### Why use Redis sorted sets and not Redis sets?
202
+
203
+ Based on the work I did in developing [leaderboard](https://github.com/agoragames/leaderboard),
204
+ leaderboards backed by Redis, I know I wanted to be able to page through the various relationships.
205
+ This does not seem to be possible given the current set of commands for Redis sets.
206
+
207
+ Also, by using the "score" in Redis sorted sets that is based on the time of when a relationship
208
+ is established, we can get our "recent friends". It is possible that the scoring function may be
209
+ user-defined in the future to allow for some specific ordering.
118
210
 
119
211
  ## Contributing to amico
120
212
 
@@ -1,28 +1,34 @@
1
1
  module Amico
2
2
  # Configuration settings for Amico.
3
3
  module Configuration
4
- # Public: Redis instance.
4
+ # Redis instance.
5
5
  attr_accessor :redis
6
6
 
7
- # Public: Amico namespace for Redis.
8
- attr_accessor :namespace
7
+ # Amico namespace for Redis.
8
+ attr_writer :namespace
9
9
 
10
- # Public: Key used in Redis for tracking who an individual is following.
11
- attr_accessor :following_key
10
+ # Key used in Redis for tracking who an individual is following.
11
+ attr_writer :following_key
12
12
 
13
- # Public: Key used in Redis for tracking the followers of an individual.
14
- attr_accessor :followers_key
13
+ # Key used in Redis for tracking the followers of an individual.
14
+ attr_writer :followers_key
15
15
 
16
- # Public: Key used in Redis for tracking who an individual blocks.
17
- attr_accessor :blocked_key
16
+ # Key used in Redis for tracking who an individual blocks.
17
+ attr_writer :blocked_key
18
18
 
19
- # Public: Key used in Redis for tracking who has reciprocated a follow for an individual.
20
- attr_accessor :reciprocated_key
19
+ # Key used in Redis for tracking who has reciprocated a follow for an individual.
20
+ attr_writer :reciprocated_key
21
21
 
22
- # Public: Page size to be used when paging through the various types of relationships.
23
- attr_accessor :page_size
22
+ # Key used in Redis for tracking pending follow relationships for an individual.
23
+ attr_writer :pending_key
24
24
 
25
- # Public: Yield self to be able to configure Amico with block-style configuration.
25
+ # Key used to indicate whether or not a follow should be pending or not.
26
+ attr_writer :pending_follow
27
+
28
+ # Page size to be used when paging through the various types of relationships.
29
+ attr_writer :page_size
30
+
31
+ # Yield self to be able to configure Amico with block-style configuration.
26
32
  #
27
33
  # Example:
28
34
  #
@@ -33,50 +39,66 @@ module Amico
33
39
  # configuration.followers_key = 'followers'
34
40
  # configuration.blocked_key = 'blocked'
35
41
  # configuration.reciprocated_key = 'reciprocated'
42
+ # configuration.pending_key = 'pending'
43
+ # configuration.pending_follow = false
36
44
  # configuration.page_size = 25
37
45
  # end
38
46
  def configure
39
47
  yield self
40
48
  end
41
49
 
42
- # Public: Amico namespace for Redis.
50
+ # Amico namespace for Redis.
43
51
  #
44
- # Returns the Amico namespace or the default of 'amico' if not set.
52
+ # @return the Amico namespace or the default of 'amico' if not set.
45
53
  def namespace
46
54
  @namespace ||= 'amico'
47
55
  end
48
56
 
49
- # Public: Key used in Redis for tracking who an individual is following.
57
+ # Key used in Redis for tracking who an individual is following.
50
58
  #
51
- # Returns the key used in Redis for tracking who an individual is following or the default of 'following' if not set.
59
+ # @return the key used in Redis for tracking who an individual is following or the default of 'following' if not set.
52
60
  def following_key
53
61
  @following_key ||= 'following'
54
62
  end
55
63
 
56
- # Public: Key used in Redis for tracking the followers of an individual.
64
+ # Key used in Redis for tracking the followers of an individual.
57
65
  #
58
- # Returns the key used in Redis for tracking the followers of an individual or the default of 'followers' if not set.
66
+ # @return the key used in Redis for tracking the followers of an individual or the default of 'followers' if not set.
59
67
  def followers_key
60
68
  @followers_key ||= 'followers'
61
69
  end
62
70
 
63
- # Public: Key used in Redis for tracking who an individual blocks.
71
+ # Key used in Redis for tracking who an individual blocks.
64
72
  #
65
- # Returns the key used in Redis for tracking who an individual blocks or the default of 'blocked' if not set.
73
+ # @return the key used in Redis for tracking who an individual blocks or the default of 'blocked' if not set.
66
74
  def blocked_key
67
75
  @blocked_key ||= 'blocked'
68
76
  end
69
77
 
70
- # Public: Key used in Redis for tracking who has reciprocated a follow for an individual.
78
+ # Key used in Redis for tracking who has reciprocated a follow for an individual.
71
79
  #
72
- # Returns the key used in Redis for tracking who has reciprocated a follow for an individual or the default of 'reciprocated' if not set.
80
+ # @return the key used in Redis for tracking who has reciprocated a follow for an individual or the default of 'reciprocated' if not set.
73
81
  def reciprocated_key
74
82
  @reciprocated_key ||= 'reciprocated'
75
83
  end
76
84
 
77
- # Public: Page size to be used when paging through the various types of relationships.
85
+ # Key used in Redis for tracking pending follow relationships for an individual.
86
+ #
87
+ # @return the key used in Redis for tracking pending follow relationships for an individual.
88
+ def pending_key
89
+ @pending_key ||= 'pending'
90
+ end
91
+
92
+ # Key used to indicate whether or not a follow should be pending or not.
93
+ #
94
+ # @return the key used to indicate whether or not a follow should be pending or not.
95
+ def pending_follow
96
+ @pending_follow ||= false
97
+ end
98
+
99
+ # Page size to be used when paging through the various types of relationships.
78
100
  #
79
- # Returns the page size to be used when paging through the various types of relationships or the default of 25 if not set.
101
+ # @return the page size to be used when paging through the various types of relationships or the default of 25 if not set.
80
102
  def page_size
81
103
  @page_size ||= 25
82
104
  end
@@ -1,11 +1,11 @@
1
1
  module Amico
2
2
  module Relationships
3
- # Public: Establish a follow relationship between two IDs. After adding the follow
4
- # relationship, it checks to see if the relationship is reciprocated and establishes that
5
- # relationship if so.
3
+ # Establish a follow relationship between two IDs. After adding the follow
4
+ # relationship, it checks to see if the relationship is reciprocated and establishes that
5
+ # relationship if so.
6
6
  #
7
- # from_id - The ID of the individual establishing the follow relationship.
8
- # to_id - The ID of the individual to be followed.
7
+ # @param from_id [String] The ID of the individual establishing the follow relationship.
8
+ # @param to_id [String] The ID of the individual to be followed.
9
9
  #
10
10
  # Examples
11
11
  #
@@ -13,26 +13,21 @@ module Amico
13
13
  def follow(from_id, to_id)
14
14
  return if from_id == to_id
15
15
  return if blocked?(to_id, from_id)
16
+ return if Amico.pending_follow && pending?(from_id, to_id)
16
17
 
17
- Amico.redis.multi do
18
- Amico.redis.zadd("#{Amico.namespace}:#{Amico.following_key}:#{from_id}", Time.now.to_i, to_id)
19
- Amico.redis.zadd("#{Amico.namespace}:#{Amico.followers_key}:#{to_id}", Time.now.to_i, from_id)
20
- end
21
-
22
- if reciprocated?(from_id, to_id)
23
- Amico.redis.multi do
24
- Amico.redis.zadd("#{Amico.namespace}:#{Amico.reciprocated_key}:#{from_id}", Time.now.to_i, to_id)
25
- Amico.redis.zadd("#{Amico.namespace}:#{Amico.reciprocated_key}:#{to_id}", Time.now.to_i, from_id)
26
- end
18
+ unless Amico.pending_follow
19
+ add_following_followers_reciprocated(from_id, to_id)
20
+ else
21
+ Amico.redis.zadd("#{Amico.namespace}:#{Amico.pending_key}:#{to_id}", Time.now.to_i, from_id)
27
22
  end
28
23
  end
29
24
 
30
- # Public: Remove a follow relationship between two IDs. After removing the follow
31
- # relationship, if a reciprocated relationship was established, it is
32
- # also removed.
25
+ # Remove a follow relationship between two IDs. After removing the follow
26
+ # relationship, if a reciprocated relationship was established, it is
27
+ # also removed.
33
28
  #
34
- # from_id - The ID of the individual removing the follow relationship.
35
- # to_id - The ID of the individual to be unfollowed.
29
+ # @param from_id [String] The ID of the individual removing the follow relationship.
30
+ # @param to_id [String] The ID of the individual to be unfollowed.
36
31
  #
37
32
  # Examples
38
33
  #
@@ -46,14 +41,15 @@ module Amico
46
41
  Amico.redis.zrem("#{Amico.namespace}:#{Amico.followers_key}:#{to_id}", from_id)
47
42
  Amico.redis.zrem("#{Amico.namespace}:#{Amico.reciprocated_key}:#{from_id}", to_id)
48
43
  Amico.redis.zrem("#{Amico.namespace}:#{Amico.reciprocated_key}:#{to_id}", from_id)
44
+ Amico.redis.zrem("#{Amico.namespace}:#{Amico.pending_key}:#{to_id}", from_id)
49
45
  end
50
- end
46
+ end
51
47
 
52
- # Public: Block a relationship between two IDs. This method also has the side effect
53
- # of removing any follower or following relationship between the two IDs.
48
+ # Block a relationship between two IDs. This method also has the side effect
49
+ # of removing any follower or following relationship between the two IDs.
54
50
  #
55
- # from_id - The ID of the individual blocking the relationship.
56
- # to_id - The ID of the individual being blocked.
51
+ # @param from_id [String] The ID of the individual blocking the relationship.
52
+ # @param to_id [String] The ID of the individual being blocked.
57
53
  #
58
54
  # Examples
59
55
  #
@@ -68,14 +64,15 @@ module Amico
68
64
  Amico.redis.zrem("#{Amico.namespace}:#{Amico.followers_key}:#{from_id}", to_id)
69
65
  Amico.redis.zrem("#{Amico.namespace}:#{Amico.reciprocated_key}:#{from_id}", to_id)
70
66
  Amico.redis.zrem("#{Amico.namespace}:#{Amico.reciprocated_key}:#{to_id}", from_id)
67
+ Amico.redis.zrem("#{Amico.namespace}:#{Amico.pending_key}:#{from_id}", to_id)
71
68
  Amico.redis.zadd("#{Amico.namespace}:#{Amico.blocked_key}:#{from_id}", Time.now.to_i, to_id)
72
69
  end
73
70
  end
74
71
 
75
- # Public: Unblock a relationship between two IDs.
72
+ # Unblock a relationship between two IDs.
76
73
  #
77
- # from_id - The ID of the individual unblocking the relationship.
78
- # to_id - The ID of the blocked individual.
74
+ # @param from_id [String] The ID of the individual unblocking the relationship.
75
+ # @param to_id [String] The ID of the blocked individual.
79
76
  #
80
77
  # Examples
81
78
  #
@@ -87,51 +84,69 @@ module Amico
87
84
  Amico.redis.zrem("#{Amico.namespace}:#{Amico.blocked_key}:#{from_id}", to_id)
88
85
  end
89
86
 
90
- # Public: Count the number of individuals that someone is following.
87
+ # Accept a relationship that is pending between two IDs.
88
+ #
89
+ # @param from_id [String] The ID of the individual accepting the relationship.
90
+ # @param to_id [String] The ID of the individual to be accepted.
91
+ #
92
+ # Example
93
+ #
94
+ # Amico.follow(1, 11)
95
+ # Amico.pending?(1, 11) # true
96
+ # Amico.accept(1, 11)
97
+ # Amico.pending?(1, 11) # false
98
+ # Amico.following?(1, 11) #true
99
+ def accept(from_id, to_id)
100
+ return if from_id == to_id
101
+
102
+ add_following_followers_reciprocated(from_id, to_id)
103
+ end
104
+
105
+ # Count the number of individuals that someone is following.
91
106
  #
92
- # id - ID of the individual to retrieve following count for.
107
+ # @param id [String] ID of the individual to retrieve following count for.
93
108
  #
94
109
  # Examples
95
110
  #
96
111
  # Amico.follow(1, 11)
97
112
  # Amico.following_count(1)
98
113
  #
99
- # Returns the count of the number of individuals that someone is following.
114
+ # @return the count of the number of individuals that someone is following.
100
115
  def following_count(id)
101
116
  Amico.redis.zcard("#{Amico.namespace}:#{Amico.following_key}:#{id}")
102
117
  end
103
118
 
104
- # Public: Count the number of individuals that are following someone.
119
+ # Count the number of individuals that are following someone.
105
120
  #
106
- # id - ID of the individual to retrieve followers count for.
121
+ # @param id [String] ID of the individual to retrieve followers count for.
107
122
  #
108
123
  # Examples
109
124
  #
110
125
  # Amico.follow(11, 1)
111
126
  # Amico.followers_count(1)
112
127
  #
113
- # Returns the count of the number of individuals that are following someone.
128
+ # @return the count of the number of individuals that are following someone.
114
129
  def followers_count(id)
115
130
  Amico.redis.zcard("#{Amico.namespace}:#{Amico.followers_key}:#{id}")
116
131
  end
117
132
 
118
- # Public: Count the number of individuals that someone has blocked.
133
+ # Count the number of individuals that someone has blocked.
119
134
  #
120
- # id - ID of the individual to retrieve blocked count for.
135
+ # @param id [String] ID of the individual to retrieve blocked count for.
121
136
  #
122
137
  # Examples
123
138
  #
124
139
  # Amico.block(1, 11)
125
140
  # Amico.blocked_count(1)
126
141
  #
127
- # Returns the count of the number of individuals that someone has blocked.
142
+ # @return the count of the number of individuals that someone has blocked.
128
143
  def blocked_count(id)
129
144
  Amico.redis.zcard("#{Amico.namespace}:#{Amico.blocked_key}:#{id}")
130
145
  end
131
146
 
132
- # Public: Count the number of individuals that have reciprocated a following relationship.
147
+ # Count the number of individuals that have reciprocated a following relationship.
133
148
  #
134
- # id - ID of the individual to retrieve reciprocated following count for.
149
+ # @param id [String] ID of the individual to retrieve reciprocated following count for.
135
150
  #
136
151
  # Examples
137
152
  #
@@ -139,59 +154,75 @@ module Amico
139
154
  # Amico.follow(11, 1)
140
155
  # Amico.reciprocated_count(1)
141
156
  #
142
- # Returns the count of the number of individuals that have reciprocated a following relationship.
157
+ # @return the count of the number of individuals that have reciprocated a following relationship.
143
158
  def reciprocated_count(id)
144
159
  Amico.redis.zcard("#{Amico.namespace}:#{Amico.reciprocated_key}:#{id}")
145
160
  end
146
161
 
147
- # Public: Check to see if one individual is following another individual.
162
+ # Count the number of relationships pending for an individual.
148
163
  #
149
- # id - ID of the individual checking the following status.
150
- # following_id - ID of the individual to see if they are being followed by id.
164
+ # @param id [String] ID of the individual to retrieve pending count for.
165
+ #
166
+ # Examples
167
+ #
168
+ # Amico.follow(11, 1)
169
+ # Amico.follow(12, 1)
170
+ # Amico.pending_count(1) # 2
171
+ #
172
+ # @return the count of the number of relationships pending for an individual.
173
+ def pending_count(id)
174
+ Amico.redis.zcard("#{Amico.namespace}:#{Amico.pending_key}:#{id}")
175
+ end
176
+
177
+ # Check to see if one individual is following another individual.
178
+ #
179
+ # @param id [String] ID of the individual checking the following status.
180
+ # @param following_id [String] ID of the individual to see if they are being followed by id.
151
181
  #
152
182
  # Examples
153
183
  #
154
184
  # Amico.follow(1, 11)
155
185
  # Amico.following?(1, 11)
156
186
  #
157
- # Returns true if id is following following_id, false otherwise
187
+ # @return true if id is following following_id, false otherwise
158
188
  def following?(id, following_id)
159
189
  !Amico.redis.zscore("#{Amico.namespace}:#{Amico.following_key}:#{id}", following_id).nil?
160
190
  end
161
191
 
162
- # Public: Check to see if one individual is a follower of another individual.
192
+ # Check to see if one individual is a follower of another individual.
163
193
  #
164
- # id - ID of the individual checking the follower status.
165
- # following_id - ID of the individual to see if they are following id.
194
+ # @param id [String] ID of the individual checking the follower status.
195
+ # @param following_id [String] ID of the individual to see if they are following id.
166
196
  #
167
197
  # Examples
168
198
  #
169
199
  # Amico.follow(11, 1)
170
200
  # Amico.follower?(1, 11)
171
- # Returns true if follower_id is following id, false otherwise
201
+ #
202
+ # @return true if follower_id is following id, false otherwise
172
203
  def follower?(id, follower_id)
173
204
  !Amico.redis.zscore("#{Amico.namespace}:#{Amico.followers_key}:#{id}", follower_id).nil?
174
205
  end
175
206
 
176
- # Public: Check to see if one individual has blocked another individual.
207
+ # Check to see if one individual has blocked another individual.
177
208
  #
178
- # id - ID of the individual checking the blocked status.
179
- # blocked_id - ID of the individual to see if they are blocked by id.
209
+ # @param id [String] ID of the individual checking the blocked status.
210
+ # @param blocked_id [String] ID of the individual to see if they are blocked by id.
180
211
  #
181
212
  # Examples
182
213
  #
183
214
  # Amico.block(1, 11)
184
215
  # Amico.blocked?(1, 11)
185
216
  #
186
- # Returns true if id has blocked blocked_id, false otherwise
217
+ # @return true if id has blocked blocked_id, false otherwise
187
218
  def blocked?(id, blocked_id)
188
219
  !Amico.redis.zscore("#{Amico.namespace}:#{Amico.blocked_key}:#{id}", blocked_id).nil?
189
220
  end
190
221
 
191
- # Public: Check to see if one individual has reciprocated in following another individual.
222
+ # Check to see if one individual has reciprocated in following another individual.
192
223
  #
193
- # from_id - ID of the individual checking the reciprocated relationship.
194
- # to_id - ID of the individual to see if they are following from_id.
224
+ # @param from_id [String] ID of the individual checking the reciprocated relationship.
225
+ # @param to_id [String] ID of the individual to see if they are following from_id.
195
226
  #
196
227
  # Examples
197
228
  #
@@ -199,16 +230,30 @@ module Amico
199
230
  # Amico.follow(11, 1)
200
231
  # Amico.reciprocated?(1, 11)
201
232
  #
202
- # Returns true if both individuals are following each other, false otherwise
233
+ # @return true if both individuals are following each other, false otherwise
203
234
  def reciprocated?(from_id, to_id)
204
235
  following?(from_id, to_id) && following?(to_id, from_id)
205
236
  end
206
237
 
207
- # Public: Retrieve a page of followed individuals for a given ID.
238
+ # Check to see if one individual has a pending relationship in following another individual.
208
239
  #
209
- # id - ID of the individual.
210
- # options - Options to be passed for retrieving a page of followed individuals.
211
- # default({:page_size => Amico.page_size, :page => 1})
240
+ # @param from_id [String] ID of the individual checking the pending relationships.
241
+ # @param to_id [String] ID of the individual to see if they are pending a follow from from_id.
242
+ #
243
+ # Examples
244
+ #
245
+ # Amico.follow(1, 11)
246
+ # Amico.pending?(1, 11) # true
247
+ #
248
+ # @return true if the relationship is pending, false otherwise
249
+ def pending?(from_id, to_id)
250
+ !Amico.redis.zscore("#{Amico.namespace}:#{Amico.pending_key}:#{to_id}", from_id).nil?
251
+ end
252
+
253
+ # Retrieve a page of followed individuals for a given ID.
254
+ #
255
+ # @param id [String] ID of the individual.
256
+ # @param options [Hash] Options to be passed for retrieving a page of followed individuals.
212
257
  #
213
258
  # Examples
214
259
  #
@@ -216,16 +261,15 @@ module Amico
216
261
  # Amico.follow(1, 12)
217
262
  # Amico.following(1, :page => 1)
218
263
  #
219
- # Returns a page of followed individuals for a given ID.
264
+ # @return a page of followed individuals for a given ID.
220
265
  def following(id, options = default_options)
221
266
  members("#{Amico.namespace}:#{Amico.following_key}:#{id}", options)
222
267
  end
223
268
 
224
- # Public: Retrieve a page of followers for a given ID.
269
+ # Retrieve a page of followers for a given ID.
225
270
  #
226
- # id - ID of the individual.
227
- # options - Options to be passed for retrieving a page of followers.
228
- # default({:page_size => Amico.page_size, :page => 1})
271
+ # @param id [String] ID of the individual.
272
+ # @param options [Hash] Options to be passed for retrieving a page of followers.
229
273
  #
230
274
  # Examples
231
275
  #
@@ -233,16 +277,15 @@ module Amico
233
277
  # Amico.follow(12, 1)
234
278
  # Amico.followers(1, :page => 1)
235
279
  #
236
- # Returns a page of followers for a given ID.
280
+ # @return a page of followers for a given ID.
237
281
  def followers(id, options = default_options)
238
282
  members("#{Amico.namespace}:#{Amico.followers_key}:#{id}", options)
239
283
  end
240
284
 
241
- # Public: Retrieve a page of blocked individuals for a given ID.
285
+ # Retrieve a page of blocked individuals for a given ID.
242
286
  #
243
- # id - ID of the individual.
244
- # options - Options to be passed for retrieving a page of blocked individuals.
245
- # default({:page_size => Amico.page_size, :page => 1})
287
+ # @param id [String] ID of the individual.
288
+ # @param options [Hash] Options to be passed for retrieving a page of blocked individuals.
246
289
  #
247
290
  # Examples
248
291
  #
@@ -250,16 +293,15 @@ module Amico
250
293
  # Amico.block(1, 12)
251
294
  # Amico.blocked(1, :page => 1)
252
295
  #
253
- # Returns a page of blocked individuals for a given ID.
296
+ # @return a page of blocked individuals for a given ID.
254
297
  def blocked(id, options = default_options)
255
298
  members("#{Amico.namespace}:#{Amico.blocked_key}:#{id}", options)
256
299
  end
257
300
 
258
- # Public: Retrieve a page of individuals that have reciprocated a follow for a given ID.
301
+ # Retrieve a page of individuals that have reciprocated a follow for a given ID.
259
302
  #
260
- # id - ID of the individual.
261
- # options - Options to be passed for retrieving a page of individuals that have reciprocated a follow.
262
- # default({:page_size => Amico.page_size, :page => 1})
303
+ # @param id [String] ID of the individual.
304
+ # @param options [Hash] Options to be passed for retrieving a page of individuals that have reciprocated a follow.
263
305
  #
264
306
  # Examples
265
307
  #
@@ -269,15 +311,31 @@ module Amico
269
311
  # Amico.follow(12, 1)
270
312
  # Amico.reciprocated(1, :page => 1)
271
313
  #
272
- # Returns a page of individuals that have reciprocated a follow for a given ID.
314
+ # @return a page of individuals that have reciprocated a follow for a given ID.
273
315
  def reciprocated(id, options = default_options)
274
316
  members("#{Amico.namespace}:#{Amico.reciprocated_key}:#{id}", options)
275
317
  end
276
318
 
277
- # Public: Count the number of pages of following relationships for an individual.
319
+ # Retrieve a page of pending relationships for a given ID.
278
320
  #
279
- # id - ID of the individual.
280
- # page_size - Page size (default: Amico.page_size).
321
+ # @param id [String] ID of the individual.
322
+ # @param options [Hash] Options to be passed for retrieving a page of pending relationships.
323
+ #
324
+ # Examples
325
+ #
326
+ # Amico.follow(1, 11)
327
+ # Amico.follow(2, 11)
328
+ # Amico.pending(1, :page => 1)
329
+ #
330
+ # @return a page of pending relationships for a given ID.
331
+ def pending(id, options = default_options)
332
+ members("#{Amico.namespace}:#{Amico.pending_key}:#{id}", options)
333
+ end
334
+
335
+ # Count the number of pages of following relationships for an individual.
336
+ #
337
+ # @param id [String] ID of the individual.
338
+ # @param page_size [int] Page size.
281
339
  #
282
340
  # Examples
283
341
  #
@@ -285,15 +343,15 @@ module Amico
285
343
  # Amico.follow(1, 12)
286
344
  # Amico.following_page_count(1)
287
345
  #
288
- # Returns the number of pages of following relationships for an individual.
346
+ # @return the number of pages of following relationships for an individual.
289
347
  def following_page_count(id, page_size = Amico.page_size)
290
348
  total_pages("#{Amico.namespace}:#{Amico.following_key}:#{id}", page_size)
291
349
  end
292
350
 
293
- # Public: Count the number of pages of follower relationships for an individual.
351
+ # Count the number of pages of follower relationships for an individual.
294
352
  #
295
- # id - ID of the individual.
296
- # page_size - Page size (default: Amico.page_size).
353
+ # @param id [String] ID of the individual.
354
+ # @param page_size [int] Page size (default: Amico.page_size).
297
355
  #
298
356
  # Examples
299
357
  #
@@ -301,15 +359,15 @@ module Amico
301
359
  # Amico.follow(12, 1)
302
360
  # Amico.followers_page_count(1)
303
361
  #
304
- # Returns the number of pages of follower relationships for an individual.
362
+ # @return the number of pages of follower relationships for an individual.
305
363
  def followers_page_count(id, page_size = Amico.page_size)
306
364
  total_pages("#{Amico.namespace}:#{Amico.followers_key}:#{id}", page_size)
307
365
  end
308
366
 
309
- # Public: Count the number of pages of blocked relationships for an individual.
367
+ # Count the number of pages of blocked relationships for an individual.
310
368
  #
311
- # id - ID of the individual.
312
- # page_size - Page size (default: Amico.page_size).
369
+ # @param id [String] ID of the individual.
370
+ # @param page_size [int] Page size (default: Amico.page_size).
313
371
  #
314
372
  # Examples
315
373
  #
@@ -317,15 +375,15 @@ module Amico
317
375
  # Amico.block(1, 12)
318
376
  # Amico.blocked_page_count(1)
319
377
  #
320
- # Returns the number of pages of blocked relationships for an individual.
378
+ # @return the number of pages of blocked relationships for an individual.
321
379
  def blocked_page_count(id, page_size = Amico.page_size)
322
380
  total_pages("#{Amico.namespace}:#{Amico.blocked_key}:#{id}", page_size)
323
381
  end
324
382
 
325
- # Public: Count the number of pages of reciprocated relationships for an individual.
383
+ # Count the number of pages of reciprocated relationships for an individual.
326
384
  #
327
- # id - ID of the individual.
328
- # page_size - Page size (default: Amico.page_size).
385
+ # @param id [String] ID of the individual.
386
+ # @param page_size [int] Page size (default: Amico.page_size).
329
387
  #
330
388
  # Examples
331
389
  #
@@ -335,36 +393,72 @@ module Amico
335
393
  # Amico.follow(12, 1)
336
394
  # Amico.reciprocated_page_count(1)
337
395
  #
338
- # Returns the number of pages of reciprocated relationships for an individual.
396
+ # @return the number of pages of reciprocated relationships for an individual.
339
397
  def reciprocated_page_count(id, page_size = Amico.page_size)
340
398
  total_pages("#{Amico.namespace}:#{Amico.reciprocated_key}:#{id}", page_size)
341
399
  end
342
400
 
401
+ # Count the number of pages of pending relationships for an individual.
402
+ #
403
+ # @param id [String] ID of the individual.
404
+ # @param page_size [int] Page size (default: Amico.page_size).
405
+ #
406
+ # Examples
407
+ #
408
+ # Amico.follow(11, 1)
409
+ # Amico.follow(12, 1)
410
+ # Amico.pending_page_count(1) # 1
411
+ #
412
+ # @return the number of pages of pending relationships for an individual.
413
+ def pending_page_count(id, page_size = Amico.page_size)
414
+ total_pages("#{Amico.namespace}:#{Amico.pending_key}:#{id}", page_size)
415
+ end
416
+
343
417
  private
344
418
 
345
- # Internal: Default options for doing, for example, paging.
419
+ # Add the following, followers and check for a reciprocated relationship. To be used from the
420
+ # +follow++ and ++accept++ methods.
421
+ #
422
+ # @param from_id [String] The ID of the individual establishing the follow relationship.
423
+ # @param to_id [String] The ID of the individual to be followed.
424
+ def add_following_followers_reciprocated(from_id, to_id)
425
+ Amico.redis.multi do
426
+ Amico.redis.zadd("#{Amico.namespace}:#{Amico.following_key}:#{from_id}", Time.now.to_i, to_id)
427
+ Amico.redis.zadd("#{Amico.namespace}:#{Amico.followers_key}:#{to_id}", Time.now.to_i, from_id)
428
+ Amico.redis.zrem("#{Amico.namespace}:#{Amico.pending_key}:#{to_id}", from_id)
429
+ end
430
+
431
+ if reciprocated?(from_id, to_id)
432
+ Amico.redis.multi do
433
+ Amico.redis.zadd("#{Amico.namespace}:#{Amico.reciprocated_key}:#{from_id}", Time.now.to_i, to_id)
434
+ Amico.redis.zadd("#{Amico.namespace}:#{Amico.reciprocated_key}:#{to_id}", Time.now.to_i, from_id)
435
+ end
436
+ end
437
+ end
438
+
439
+ # Default options for doing, for example, paging.
346
440
  #
347
- # Returns a hash of the default options.
441
+ # @return a hash of the default options.
348
442
  def default_options
349
443
  {:page_size => Amico.page_size, :page => 1}
350
444
  end
351
445
 
352
- # Internal: Count the total number of pages for a given key in a Redis sorted set.
446
+ # Count the total number of pages for a given key in a Redis sorted set.
353
447
  #
354
- # key - Redis key.
355
- # page_size - Page size from which to calculate total pages.
448
+ # @param key [String] Redis key.
449
+ # @param page_size [int] Page size from which to calculate total pages.
356
450
  #
357
- # Returns total number of pages for a given key in a Redis sorted set.
451
+ # @return total number of pages for a given key in a Redis sorted set.
358
452
  def total_pages(key, page_size)
359
453
  (Amico.redis.zcard(key) / page_size.to_f).ceil
360
454
  end
361
455
 
362
- # Internal: Retrieve a page of items from a Redis sorted set without scores.
456
+ # Retrieve a page of items from a Redis sorted set without scores.
363
457
  #
364
- # key - Redis key.
365
- # options - Default options for paging (default: {:page_size => Amico.page_size, :page => 1})
458
+ # @param key [String] Redis key.
459
+ # @param options [Hash] Default options for paging.
366
460
  #
367
- # Returns a page of items from a Redis sorted set without scores.
461
+ # @return a page of items from a Redis sorted set without scores.
368
462
  def members(key, options = default_options)
369
463
  options = default_options.dup.merge!(options)
370
464
  if options[:page] < 1
@@ -1,3 +1,3 @@
1
1
  module Amico
2
- VERSION = '1.1.0'
2
+ VERSION = '1.2.0'
3
3
  end
@@ -9,6 +9,8 @@ describe Amico::Configuration do
9
9
  configuration.followers_key.should eql('followers')
10
10
  configuration.blocked_key.should eql('blocked')
11
11
  configuration.reciprocated_key.should eql('reciprocated')
12
+ configuration.pending_key.should eql('pending')
13
+ configuration.pending_follow.should be_false
12
14
  configuration.page_size.should be(25)
13
15
  end
14
16
  end
@@ -269,6 +269,127 @@ describe Amico::Relationships do
269
269
  end
270
270
  end
271
271
 
272
+ describe 'pending_follow enabled' do
273
+ before(:each) do
274
+ Amico.pending_follow = true
275
+ end
276
+
277
+ after(:each) do
278
+ Amico.pending_follow = false
279
+ end
280
+
281
+ describe '#follow' do
282
+ it 'should allow you to follow but the relationship is initially pending' do
283
+ Amico.follow(1, 11)
284
+
285
+ Amico.redis.zcard("#{Amico.namespace}:#{Amico.following_key}:1").should be(0)
286
+ Amico.redis.zcard("#{Amico.namespace}:#{Amico.followers_key}:11").should be(0)
287
+ Amico.redis.zcard("#{Amico.namespace}:#{Amico.pending_key}:11").should be(1)
288
+ end
289
+
290
+ it 'should remove the pending relationship if you have a pending follow, but you unfollow' do
291
+ Amico.follow(1, 11)
292
+
293
+ Amico.redis.zcard("#{Amico.namespace}:#{Amico.following_key}:1").should be(0)
294
+ Amico.redis.zcard("#{Amico.namespace}:#{Amico.followers_key}:11").should be(0)
295
+ Amico.redis.zcard("#{Amico.namespace}:#{Amico.pending_key}:11").should be(1)
296
+
297
+ Amico.unfollow(1, 11)
298
+
299
+ Amico.redis.zcard("#{Amico.namespace}:#{Amico.following_key}:1").should be(0)
300
+ Amico.redis.zcard("#{Amico.namespace}:#{Amico.followers_key}:11").should be(0)
301
+ Amico.redis.zcard("#{Amico.namespace}:#{Amico.pending_key}:11").should be(0)
302
+ end
303
+
304
+ it 'should remove the pending relationship and add to following and followers if #accept is called' do
305
+ Amico.follow(1, 11)
306
+ Amico.pending?(1, 11).should be_true
307
+
308
+ Amico.accept(1, 11)
309
+
310
+ Amico.pending?(1, 11).should be_false
311
+ Amico.following?(1, 11).should be_true
312
+ Amico.following?(11, 1).should be_false
313
+ Amico.follower?(11, 1).should be_true
314
+ Amico.follower?(1, 11).should be_false
315
+ end
316
+
317
+ it 'should remove the pending relationship and add to following and followers if #accept is called and add to reciprocated relationship' do
318
+ Amico.follow(1, 11)
319
+ Amico.follow(11, 1)
320
+ Amico.pending?(1, 11).should be_true
321
+ Amico.pending?(11, 1).should be_true
322
+
323
+ Amico.accept(1, 11)
324
+
325
+ Amico.pending?(1, 11).should be_false
326
+ Amico.pending?(11, 1).should be_true
327
+ Amico.following?(1, 11).should be_true
328
+ Amico.following?(11, 1).should be_false
329
+ Amico.follower?(11, 1).should be_true
330
+ Amico.follower?(1, 11).should be_false
331
+
332
+ Amico.accept(11, 1)
333
+
334
+ Amico.pending?(1, 11).should be_false
335
+ Amico.pending?(11, 1).should be_false
336
+ Amico.following?(1, 11).should be_true
337
+ Amico.following?(11, 1).should be_true
338
+ Amico.follower?(11, 1).should be_true
339
+ Amico.follower?(1, 11).should be_true
340
+ Amico.reciprocated?(1, 11).should be_true
341
+ end
342
+ end
343
+
344
+ describe '#block' do
345
+ it 'should remove the pending relationship if you block someone' do
346
+ Amico.follow(11, 1)
347
+ Amico.pending?(11, 1).should be_true
348
+ Amico.block(1, 11)
349
+ Amico.pending?(11, 1).should be_false
350
+ Amico.blocked?(1, 11).should be_true
351
+ end
352
+ end
353
+
354
+ describe '#pending' do
355
+ it 'should return the correct list' do
356
+ Amico.follow(1, 11)
357
+ Amico.follow(11, 1)
358
+ Amico.pending(1).should eql(["11"])
359
+ Amico.pending(11).should eql(["1"])
360
+ end
361
+
362
+ it 'should page correctly' do
363
+ add_reciprocal_followers
364
+
365
+ Amico.pending(1, :page => 1, :page_size => 5).size.should be(5)
366
+ Amico.pending(1, :page => 1, :page_size => 10).size.should be(10)
367
+ Amico.pending(1, :page => 1, :page_size => 26).size.should be(25)
368
+ end
369
+ end
370
+
371
+ describe '#pending_count' do
372
+ it 'should return the correct count' do
373
+ Amico.follow(1, 11)
374
+ Amico.follow(11, 1)
375
+ Amico.follow(1, 12)
376
+ Amico.follow(12, 1)
377
+ Amico.follow(1, 13)
378
+ Amico.pending_count(1).should be(2)
379
+ end
380
+ end
381
+
382
+ describe '#pending_page_count' do
383
+ it 'should return the correct count' do
384
+ add_reciprocal_followers
385
+
386
+ Amico.pending_page_count(1).should be(1)
387
+ Amico.pending_page_count(1, 10).should be(3)
388
+ Amico.pending_page_count(1, 5).should be(5)
389
+ end
390
+ end
391
+ end
392
+
272
393
  private
273
394
 
274
395
  def add_reciprocal_followers(count = 26, block_relationship = false)
@@ -4,11 +4,19 @@ require 'amico'
4
4
  Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
5
5
 
6
6
  RSpec.configure do |config|
7
- config.before(:each) do
7
+ config.before(:all) do
8
8
  Amico.configure do |configuration|
9
- redis = Redis.new
10
- redis.flushall
9
+ redis = Redis.new(:db => 15)
11
10
  configuration.redis = redis
12
11
  end
13
12
  end
13
+
14
+ config.before(:each) do
15
+ Amico.redis.flushdb
16
+ end
17
+
18
+ config.after(:all) do
19
+ Amico.redis.flushdb
20
+ Amico.redis.quit
21
+ end
14
22
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: amico
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-01-13 00:00:00.000000000 Z
12
+ date: 2012-02-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: redis
16
- requirement: &2164475940 !ruby/object:Gem::Requirement
16
+ requirement: &2157986940 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *2164475940
24
+ version_requirements: *2157986940
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rake
27
- requirement: &2164475520 !ruby/object:Gem::Requirement
27
+ requirement: &2157986520 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *2164475520
35
+ version_requirements: *2157986520
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rspec
38
- requirement: &2164475100 !ruby/object:Gem::Requirement
38
+ requirement: &2157986100 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,7 +43,7 @@ dependencies:
43
43
  version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *2164475100
46
+ version_requirements: *2157986100
47
47
  description: Relationships (e.g. friendships) backed by Redis
48
48
  email:
49
49
  - dczarnecki@agoragames.com
@@ -81,7 +81,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
81
81
  version: '0'
82
82
  segments:
83
83
  - 0
84
- hash: 2967547926380313375
84
+ hash: 4319511261827775332
85
85
  required_rubygems_version: !ruby/object:Gem::Requirement
86
86
  none: false
87
87
  requirements:
@@ -90,7 +90,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
90
90
  version: '0'
91
91
  segments:
92
92
  - 0
93
- hash: 2967547926380313375
93
+ hash: 4319511261827775332
94
94
  requirements: []
95
95
  rubyforge_project: amico
96
96
  rubygems_version: 1.8.10