amico 1.1.0 → 1.2.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/.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