flocks 0.0.1
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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +25 -0
- data/README.md +142 -0
- data/Rakefile +11 -0
- data/flocks.gemspec +26 -0
- data/lib/flocks.rb +23 -0
- data/lib/flocks/configuration.rb +113 -0
- data/lib/flocks/ordering.rb +33 -0
- data/lib/flocks/relationships.rb +87 -0
- data/lib/flocks/search.rb +37 -0
- data/lib/flocks/version.rb +3 -0
- data/test/flocks/configuration_test.rb +23 -0
- data/test/flocks/flocks_test.rb +10 -0
- data/test/flocks/ordering_test.rb +12 -0
- data/test/flocks/relationships_test.rb +55 -0
- data/test/flocks/search_test.rb +36 -0
- data/test/test_helper.rb +28 -0
- metadata +124 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9670cef0827448591a9929ae6b1e29e9a10907d8
|
4
|
+
data.tar.gz: f44b3a80e9ba291f79472cb32f207de3e3fd2fda
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b1bee72695582429793848aa7ae8694c6f05e5f8710ae2cf14e9f79baf0ecf62afc395e4fc1c316676f57a393f70a0dbc74413ddc7167480e630b3c9b56c460d
|
7
|
+
data.tar.gz: ac2b6281eb80f9853680c5312358035be1af26e6794aafa4f25695470a7ccd1d69e9fe4722ca45f5ab7e6c8b1963fe1e374771f40e5aa1cff145744132cc768b
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
Copyright (c) 2014, Byliner, Inc
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without modification,
|
5
|
+
are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
* Redistributions of source code must retain the above copyright notice, this
|
8
|
+
list of conditions and the following disclaimer.
|
9
|
+
* Redistributions in binary form must reproduce the above copyright notice, this
|
10
|
+
list of conditions and the following disclaimer in the documentation and/or
|
11
|
+
other materials provided with the distribution.
|
12
|
+
* Neither the name of the DUO Interactive, LLC nor the names of its contributors
|
13
|
+
may be used to endorse or promote products derived from this software without
|
14
|
+
specific prior written permission.
|
15
|
+
|
16
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
17
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
18
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
19
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
20
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
21
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
22
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
23
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
24
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
25
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
# Flocks
|
2
|
+
|
3
|
+
Flocks is a lightweight searchable social graph that tracks a user's followers, who they are following, and who they have blocked.
|
4
|
+
|
5
|
+
Flocks uses [Redis](http://redis.io/documentation) to store the relationships.
|
6
|
+
|
7
|
+
User lists are sorted alphabetically and can be searched!
|
8
|
+
|
9
|
+
Flocks uses usernames (or any unique string identifier) when following in order to sort the following/followers lists.
|
10
|
+
By requiring usernames when establishing relationships, Flocks becomes searchable.
|
11
|
+
Flocks will return all user ids with user_ids matching the query string.
|
12
|
+
Flocks search capabilities can be used to autocomplete text fields with usernames in a user's social graph.
|
13
|
+
|
14
|
+
For example, if a user is following: 'Adam', 'Aaron', 'Addison', and 'John'
|
15
|
+
|
16
|
+
Searching for 'a' will return the ids associated with Adam, Aaron, and Addison
|
17
|
+
Searching for 'ad' will return the id associated with Adam
|
18
|
+
|
19
|
+
|
20
|
+
## Installation
|
21
|
+
|
22
|
+
Add this line to your application's Gemfile:
|
23
|
+
|
24
|
+
gem 'flocks'
|
25
|
+
|
26
|
+
And then execute:
|
27
|
+
|
28
|
+
$ bundle
|
29
|
+
|
30
|
+
Or install it yourself as:
|
31
|
+
|
32
|
+
$ gem install flocks
|
33
|
+
|
34
|
+
## Usage
|
35
|
+
|
36
|
+
Configure flocks:
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
Flocks.configure do |configuration|
|
40
|
+
configuration.redis = Redis.new
|
41
|
+
configuration.namespace = 'flocks'
|
42
|
+
configuration.following_key = 'following'
|
43
|
+
configuration.followers_key = 'followers'
|
44
|
+
configuration.blocked_key = 'blocked'
|
45
|
+
configuration.page_size = 25
|
46
|
+
end
|
47
|
+
```
|
48
|
+
|
49
|
+
Use flocks:
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
require 'flocks'
|
53
|
+
=> true
|
54
|
+
|
55
|
+
Flocks.configure do |configuration|
|
56
|
+
configuration.redis = Redis.new
|
57
|
+
configuration.namespace = 'flocks'
|
58
|
+
configuration.following_key = 'following'
|
59
|
+
configuration.followers_key = 'followers'
|
60
|
+
configuration.blocked_key = 'blocked'
|
61
|
+
configuration.page_size = 25
|
62
|
+
end
|
63
|
+
|
64
|
+
Flocks.new(1, 'username').follow(11, 'other_username')
|
65
|
+
=> true
|
66
|
+
|
67
|
+
Flocks.new(1, 'username').following?(11)
|
68
|
+
=> true
|
69
|
+
|
70
|
+
Flocks.new(11, 'other_username').following?(1)
|
71
|
+
=> false
|
72
|
+
|
73
|
+
Flocks.new(11, 'other_username').follow(1, 'username')
|
74
|
+
=> true
|
75
|
+
|
76
|
+
Flocks.new(11, 'other_username').following?(1)
|
77
|
+
=> true
|
78
|
+
|
79
|
+
Flocks.new(1, 'username').following_count
|
80
|
+
=> 1
|
81
|
+
|
82
|
+
Flocks.new(1, 'username').followers_count
|
83
|
+
=> 1
|
84
|
+
|
85
|
+
Flocks.new(11, 'other_username').unfollow(1)
|
86
|
+
=> true
|
87
|
+
|
88
|
+
Flocks.new(11, 'other_username').following_count
|
89
|
+
=> 0
|
90
|
+
|
91
|
+
Flocks.new(1, 'username').following_count
|
92
|
+
=> 1
|
93
|
+
|
94
|
+
Flocks.new(1, 'username').following
|
95
|
+
=> ["11"]
|
96
|
+
|
97
|
+
Flocks.new(1, 'username').block(11)
|
98
|
+
=> true
|
99
|
+
|
100
|
+
Flocks.new(11, 'username').following?(1)
|
101
|
+
=> false
|
102
|
+
|
103
|
+
Flocks.new(1, 'username').blocked?(11)
|
104
|
+
=> true
|
105
|
+
|
106
|
+
Flocks.new(11, 'other_username').follow(1)
|
107
|
+
=> nil
|
108
|
+
|
109
|
+
Flocks.new(1, 'username').unblock(11)
|
110
|
+
=> true
|
111
|
+
|
112
|
+
Flocks.new(1, 'username').blocked?(11)
|
113
|
+
=> false
|
114
|
+
|
115
|
+
Flocks.new(11, 'other_username').follow(1)
|
116
|
+
=> true
|
117
|
+
|
118
|
+
Flocks.new(1, 'username').follow(11)
|
119
|
+
=> true
|
120
|
+
|
121
|
+
Flocks.new(1, 'username').search_following('other')
|
122
|
+
=> ["11"]
|
123
|
+
|
124
|
+
Flocks.new(1, 'username').search_followers('other')
|
125
|
+
=> ["11"]
|
126
|
+
|
127
|
+
Flocks.new(1, 'username').search_graph('other')
|
128
|
+
=> ["11"]
|
129
|
+
|
130
|
+
```
|
131
|
+
|
132
|
+
## Thanks
|
133
|
+
|
134
|
+
Many thanks to [amico](https://github.com/agoragames/amico) creator [agoragames](https://github.com/agoragames). A lot of the code used was inspired by the [amico gem](https://github.com/agoragames/amico).
|
135
|
+
|
136
|
+
## Contributing
|
137
|
+
|
138
|
+
1. Fork it ( http://github.com/<my-github-username>/flocks/fork )
|
139
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
140
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
141
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
142
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/flocks.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'flocks/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "flocks"
|
8
|
+
spec.version = Flocks::VERSION
|
9
|
+
spec.authors = ["Adam Ryan"]
|
10
|
+
spec.email = ["adam.g.ryan@gmail.com"]
|
11
|
+
spec.summary = %q{Single node social relationships using redis.}
|
12
|
+
spec.description = %q{Single node social relationships using redis.}
|
13
|
+
spec.homepage = "https://github.com/byliner/flocks"
|
14
|
+
spec.license = "BSD"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "redis"
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
24
|
+
spec.add_development_dependency "rake"
|
25
|
+
spec.add_development_dependency "minitest"
|
26
|
+
end
|
data/lib/flocks.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'redis'
|
2
|
+
require 'flocks/configuration'
|
3
|
+
require 'flocks/ordering'
|
4
|
+
require 'flocks/relationships'
|
5
|
+
require 'flocks/search'
|
6
|
+
require 'flocks/version'
|
7
|
+
|
8
|
+
module Flocks
|
9
|
+
class << self
|
10
|
+
include Configuration
|
11
|
+
include Ordering
|
12
|
+
include Search
|
13
|
+
include Relationships
|
14
|
+
|
15
|
+
attr_accessor :user_id, :username_score
|
16
|
+
|
17
|
+
def new(user_id, username = nil)
|
18
|
+
self.user_id = user_id
|
19
|
+
self.username_score = rank_username(username)
|
20
|
+
self
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module Flocks
|
2
|
+
# Configuration settings for Flocks.
|
3
|
+
module Configuration
|
4
|
+
# Redis instance.
|
5
|
+
attr_accessor :redis
|
6
|
+
|
7
|
+
# Flocks namespace for Redis.
|
8
|
+
attr_writer :namespace
|
9
|
+
|
10
|
+
# Key used in Redis for tracking who an individual is following.
|
11
|
+
attr_writer :following_key
|
12
|
+
|
13
|
+
# Key used in Redis for tracking the followers of an individual.
|
14
|
+
attr_writer :followers_key
|
15
|
+
|
16
|
+
# Key used in Redis for tracking who an individual blocks.
|
17
|
+
attr_writer :blocked_key
|
18
|
+
|
19
|
+
# Key used in Redis for tracking who has blocked an individual.
|
20
|
+
attr_writer :blocked_by_key
|
21
|
+
|
22
|
+
# Key used in Redis for tracking who has reciprocated a follow for an individual.
|
23
|
+
attr_writer :reciprocated_key
|
24
|
+
|
25
|
+
# Key used in Redis for tracking pending follow relationships for an individual.
|
26
|
+
attr_writer :pending_key
|
27
|
+
|
28
|
+
# Key used in Redis for tracking who an individual is awaiting approval from.
|
29
|
+
attr_writer :pending_with_key
|
30
|
+
|
31
|
+
# Key used to indicate whether or not a follow should be pending or not.
|
32
|
+
attr_writer :pending_follow
|
33
|
+
|
34
|
+
# Page size to be used when paging through the various types of relationships.
|
35
|
+
attr_writer :page_size
|
36
|
+
|
37
|
+
# Set the percision for relationship sorting granularity
|
38
|
+
attr_writer :string_score_percision
|
39
|
+
|
40
|
+
# Yield self to be able to configure Flocks with block-style configuration.
|
41
|
+
#
|
42
|
+
# Example:
|
43
|
+
#
|
44
|
+
# Flocks.configure do |configuration|
|
45
|
+
# configuration.redis = Redis.new
|
46
|
+
# configuration.namespace = 'flocks'
|
47
|
+
# configuration.following_key = 'following'
|
48
|
+
# configuration.followers_key = 'followers'
|
49
|
+
# configuration.blocked_key = 'blocked'
|
50
|
+
# configuration.blocked_by_key = 'blocked_by'
|
51
|
+
# configuration.page_size = 25
|
52
|
+
# end
|
53
|
+
def configure
|
54
|
+
yield self
|
55
|
+
end
|
56
|
+
|
57
|
+
# Flocks namespace for Redis.
|
58
|
+
#
|
59
|
+
# @return the Flocks namespace or the default of 'flocks' if not set.
|
60
|
+
def namespace
|
61
|
+
@namespace ||= 'flocks'
|
62
|
+
end
|
63
|
+
|
64
|
+
# Key used in Redis for tracking who an individual is following.
|
65
|
+
#
|
66
|
+
# @return the key used in Redis for tracking who an individual is following or the default of 'following' if not set.
|
67
|
+
def following_key
|
68
|
+
@following_key ||= 'following'
|
69
|
+
end
|
70
|
+
|
71
|
+
# Key used in Redis for tracking the followers of an individual.
|
72
|
+
#
|
73
|
+
# @return the key used in Redis for tracking the followers of an individual or the default of 'followers' if not set.
|
74
|
+
def followers_key
|
75
|
+
@followers_key ||= 'followers'
|
76
|
+
end
|
77
|
+
|
78
|
+
# Key used in Redis for tracking who an individual blocks.
|
79
|
+
#
|
80
|
+
# @return the key used in Redis for tracking who an individual blocks or the default of 'blocked' if not set.
|
81
|
+
def blocked_key
|
82
|
+
@blocked_key ||= 'blocked'
|
83
|
+
end
|
84
|
+
|
85
|
+
# Key used in Redis for tracking who has blocked an individual.
|
86
|
+
#
|
87
|
+
# @return the key used in Redis for tracking who has blocked an individual or the default of 'blocked_by' if not set.
|
88
|
+
def blocked_by_key
|
89
|
+
@blocked_by_key ||= 'blocked_by'
|
90
|
+
end
|
91
|
+
|
92
|
+
# Key used in Redis for tracking who has reciprocated a follow for an individual.
|
93
|
+
#
|
94
|
+
# @return the key used in Redis for tracking who has reciprocated a follow for an individual or the default of 'reciprocated' if not set.
|
95
|
+
def reciprocated_key
|
96
|
+
@reciprocated_key ||= 'reciprocated'
|
97
|
+
end
|
98
|
+
|
99
|
+
# Page size to be used when paging through the various types of relationships.
|
100
|
+
#
|
101
|
+
# @return the page size to be used when paging through the various types of relationships or the default of 25 if not set.
|
102
|
+
def page_size
|
103
|
+
@page_size ||= 25
|
104
|
+
end
|
105
|
+
|
106
|
+
# Number of characters to be used when scoring a username for indexing in redis
|
107
|
+
#
|
108
|
+
# @return the scoring percision
|
109
|
+
def string_score_percision
|
110
|
+
@string_score_percision ||= 5
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Flocks
|
2
|
+
module Ordering
|
3
|
+
def rank_username(username)
|
4
|
+
# Selects the number of characters to be evaluated for sorting
|
5
|
+
percision_index = string_score_percision - 1
|
6
|
+
|
7
|
+
# Base score
|
8
|
+
score = 0
|
9
|
+
|
10
|
+
# Get the first 5 characters in an array
|
11
|
+
characters = username.to_s.downcase[0..percision_index].split('')
|
12
|
+
|
13
|
+
# If the username is short, tack on the lowest ascii value
|
14
|
+
characters << '0' until characters.size == string_score_percision
|
15
|
+
|
16
|
+
# Reverse them for easy multiplication scoring
|
17
|
+
characters.reverse.each_with_index do |char, i|
|
18
|
+
|
19
|
+
# Use the mutiplier, increase magnitude for weighting first letters more
|
20
|
+
# multiplier = (character_multiplier[0..(i + 1)].to_i)
|
21
|
+
|
22
|
+
multiplier = 10 ** i
|
23
|
+
# Square the multiplier to avoid large ascii values from thwrowing off calculations
|
24
|
+
char_score = "#{char}"[0].ord * multiplier * multiplier
|
25
|
+
|
26
|
+
# Aggregate the swcore
|
27
|
+
score += char_score
|
28
|
+
end
|
29
|
+
|
30
|
+
score
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module Flocks
|
2
|
+
module Relationships
|
3
|
+
|
4
|
+
def relationship_following_key
|
5
|
+
"#{following_key}/#{user_id}"
|
6
|
+
end
|
7
|
+
|
8
|
+
def relationship_followers_key(id = user_id)
|
9
|
+
"#{followers_key}/#{id}"
|
10
|
+
end
|
11
|
+
|
12
|
+
def relationship_blocked_key
|
13
|
+
"#{blocked_key}/#{user_id}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def follow(other_user_id, other_username)
|
17
|
+
return if (user_id == other_user_id) || (blocked? other_user_id)
|
18
|
+
|
19
|
+
other_username_score = rank_username(other_username)
|
20
|
+
redis.zadd relationship_following_key, other_username_score, other_user_id
|
21
|
+
redis.zadd relationship_followers_key(other_user_id), username_score, user_id
|
22
|
+
end
|
23
|
+
|
24
|
+
def unfollow(other_user_id)
|
25
|
+
redis.zrem relationship_following_key, other_user_id
|
26
|
+
redis.zrem relationship_followers_key(other_user_id), user_id
|
27
|
+
end
|
28
|
+
|
29
|
+
def following(page = 0, limit = 0)
|
30
|
+
# Without arguments returns the full set
|
31
|
+
start, stop = bounds(page, limit)
|
32
|
+
redis.zrange relationship_following_key, start, stop
|
33
|
+
end
|
34
|
+
|
35
|
+
def followers(page = 0, limit= 0)
|
36
|
+
start, stop = bounds(page, limit)
|
37
|
+
redis.zrange relationship_followers_key(user_id), start, stop
|
38
|
+
end
|
39
|
+
|
40
|
+
def following?(other_user_id)
|
41
|
+
following.include? other_user_id.to_s
|
42
|
+
end
|
43
|
+
|
44
|
+
def following_count
|
45
|
+
redis.zcard relationship_following_key
|
46
|
+
end
|
47
|
+
|
48
|
+
def followers_count
|
49
|
+
redis.zcard relationship_followers_key
|
50
|
+
end
|
51
|
+
|
52
|
+
def following_page_count(limit = page_size)
|
53
|
+
(following_count.to_f / limit.to_f).ceil
|
54
|
+
end
|
55
|
+
|
56
|
+
def followers_page_count(limit = page_size)
|
57
|
+
(followers_count.to_f / limit.to_f).ceil
|
58
|
+
end
|
59
|
+
|
60
|
+
def block(other_user_id)
|
61
|
+
# Unfollow
|
62
|
+
unfollow other_user_id
|
63
|
+
self.new(other_user_id).unfollow user_id
|
64
|
+
|
65
|
+
# Block
|
66
|
+
redis.sadd relationship_blocked_key, other_user_id
|
67
|
+
end
|
68
|
+
|
69
|
+
def unblock(other_user_id)
|
70
|
+
redis.srem relationship_blocked_key, other_user_id
|
71
|
+
end
|
72
|
+
|
73
|
+
def blocked
|
74
|
+
redis.smembers relationship_blocked_key
|
75
|
+
end
|
76
|
+
|
77
|
+
def blocked?(other_user_id)
|
78
|
+
blocked.include? other_user_id.to_s
|
79
|
+
end
|
80
|
+
|
81
|
+
protected
|
82
|
+
|
83
|
+
def bounds(page, limit)
|
84
|
+
[(page - 1) * limit, (page * limit) - 1]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Flocks
|
2
|
+
module Search
|
3
|
+
|
4
|
+
# Search a user's list of following
|
5
|
+
def search_following(query)
|
6
|
+
redis.zrangebyscore relationship_following_key, start_score(query), stop_score(query)
|
7
|
+
end
|
8
|
+
|
9
|
+
# Search a user's list of followers
|
10
|
+
def search_followers(query)
|
11
|
+
redis.zrangebyscore relationship_followers_key, start_score(query), stop_score(query)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Search a user's entire social graph
|
15
|
+
def search_graph(query)
|
16
|
+
(search_following(query) + search_followers(query)).uniq
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
|
21
|
+
# Set constraints to return relevent results
|
22
|
+
# Starting score for redis zrangebyscore
|
23
|
+
def start_score(query)
|
24
|
+
rank_username(query)
|
25
|
+
end
|
26
|
+
|
27
|
+
# 'aaa' -> 'aab'
|
28
|
+
# Ending score for redis zrangebyscore
|
29
|
+
def stop_score(query)
|
30
|
+
query_array = query.chars
|
31
|
+
last_char = (query_array.pop.ord + 1).chr
|
32
|
+
stop_name = query_array.push(last_char).join('')
|
33
|
+
rank_username(stop_name)
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ConfigurationTest < MiniTest::Test
|
4
|
+
|
5
|
+
def test_configuration
|
6
|
+
Flocks.configure do |config|
|
7
|
+
config.redis = Redis.new
|
8
|
+
config.namespace = 'flocks_test'
|
9
|
+
config.following_key = 'following_test'
|
10
|
+
config.followers_key = 'followers_test'
|
11
|
+
config.blocked_key = 'blocked_test'
|
12
|
+
config.blocked_by_key = 'blocked_by_test'
|
13
|
+
config.page_size = 50
|
14
|
+
end
|
15
|
+
|
16
|
+
assert_equal 'flocks_test', Flocks.namespace
|
17
|
+
assert_equal 'following_test', Flocks.following_key
|
18
|
+
assert_equal 'followers_test', Flocks.followers_key
|
19
|
+
assert_equal 'blocked_test', Flocks.blocked_key
|
20
|
+
assert_equal 'blocked_by_test', Flocks.blocked_by_key
|
21
|
+
assert_equal 50, Flocks.page_size
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class OrderingTest < MiniTest::Test
|
4
|
+
include Flocks::Ordering
|
5
|
+
include Flocks::Configuration
|
6
|
+
|
7
|
+
def test_score_string_extension
|
8
|
+
# ASCII 'd' == 100, summed up in the scorer == 10101010100
|
9
|
+
assert_equal 10101010100, rank_username('ddddd')
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class RelationshipsTest < MiniTest::Test
|
4
|
+
|
5
|
+
def test_follow_and_unfollow
|
6
|
+
flock = Flocks.new(1, 'username')
|
7
|
+
flock.follow(2, 'other_username')
|
8
|
+
assert flock.following?(2)
|
9
|
+
|
10
|
+
flock.unfollow(2)
|
11
|
+
refute flock.following?(2)
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_following_and_following_count
|
15
|
+
flock = Flocks.new(1, 'username')
|
16
|
+
flock.follow(2, 'other_username')
|
17
|
+
assert_equal ['2'], flock.following
|
18
|
+
assert_equal 1, flock.following_count
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_followers_and_followers_count
|
22
|
+
flock = Flocks.new(1, 'username')
|
23
|
+
flock.follow(2, 'other_username')
|
24
|
+
|
25
|
+
other_flock = Flocks.new(2, 'other_username')
|
26
|
+
assert_equal ['1'], other_flock.followers
|
27
|
+
assert_equal 1, other_flock.followers_count
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_following_page_count
|
31
|
+
flock = Flocks.new(1, 'username')
|
32
|
+
(2..40).to_a.each { |i| flock.follow(i, "#{i}") }
|
33
|
+
assert_equal 2, flock.following_page_count
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_followers_page_count
|
37
|
+
(2..40).to_a.each { |i| Flocks.new(i, "#{i}").follow(1, "username") }
|
38
|
+
flock = Flocks.new(1, 'username')
|
39
|
+
assert_equal 2, flock.followers_page_count
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_block_blocked_and_unblock
|
43
|
+
flock = Flocks.new(1, 'username')
|
44
|
+
flock.follow(2, 'other_username')
|
45
|
+
|
46
|
+
flock.block(2)
|
47
|
+
assert_equal ['2'], flock.blocked
|
48
|
+
assert flock.blocked? 2
|
49
|
+
refute Flocks.new(2).following? 1
|
50
|
+
|
51
|
+
flock.unblock(2)
|
52
|
+
assert_equal ([]), flock.blocked
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class SearchTest < MiniTest::Test
|
4
|
+
|
5
|
+
def test_search_following
|
6
|
+
flock = Flocks.new(1, 'username')
|
7
|
+
flock.follow(2, 'other_username')
|
8
|
+
flock.follow(3, 'other_username2')
|
9
|
+
flock.follow(4, 'dissimilar_username')
|
10
|
+
results = flock.search_following('other')
|
11
|
+
|
12
|
+
assert_equal ['2', '3'], results
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_search_followers
|
16
|
+
Flocks.new(2, 'other_username').follow(1, 'username')
|
17
|
+
Flocks.new(3, 'other_username2').follow(1, 'username')
|
18
|
+
Flocks.new(4, 'disssimilar_username').follow(1, 'username')
|
19
|
+
|
20
|
+
flock = Flocks.new(1, 'username')
|
21
|
+
results = flock.search_followers('other')
|
22
|
+
assert_equal ['2', '3'], results
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_search_graph
|
26
|
+
Flocks.new(1, 'username').follow(2, 'other_username')
|
27
|
+
Flocks.new(2, 'other_username').follow(1, 'username')
|
28
|
+
Flocks.new(1, 'username').follow(3, 'other_username3')
|
29
|
+
Flocks.new(4, 'other_username4').follow(1, 'username')
|
30
|
+
Flocks.new(5, 'disssimilar_username').follow(1, 'username')
|
31
|
+
|
32
|
+
flock = Flocks.new(1, 'username')
|
33
|
+
results = flock.search_graph('other')
|
34
|
+
assert_equal ['2', '3', '4'], results
|
35
|
+
end
|
36
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
Bundler.require :test
|
4
|
+
|
5
|
+
require 'minitest/autorun'
|
6
|
+
require 'redis'
|
7
|
+
require 'flocks'
|
8
|
+
|
9
|
+
# Support files
|
10
|
+
Dir["#{File.expand_path(File.dirname(__FILE__))}/support/*.rb"].each do |file|
|
11
|
+
require file
|
12
|
+
end
|
13
|
+
|
14
|
+
class MiniTest::Test
|
15
|
+
def setup
|
16
|
+
Flocks.configure do |config|
|
17
|
+
config.redis = Redis.new
|
18
|
+
config.namespace = 'flocks'
|
19
|
+
config.following_key = 'following'
|
20
|
+
config.followers_key = 'followers'
|
21
|
+
config.blocked_key = 'blocked'
|
22
|
+
config.blocked_by_key = 'blocked_by'
|
23
|
+
config.page_size = 25
|
24
|
+
end
|
25
|
+
|
26
|
+
Flocks.redis.flushdb
|
27
|
+
end
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: flocks
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Adam Ryan
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-05-07 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: redis
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.5'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.5'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: Single node social relationships using redis.
|
70
|
+
email:
|
71
|
+
- adam.g.ryan@gmail.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- ".gitignore"
|
77
|
+
- Gemfile
|
78
|
+
- LICENSE.txt
|
79
|
+
- README.md
|
80
|
+
- Rakefile
|
81
|
+
- flocks.gemspec
|
82
|
+
- lib/flocks.rb
|
83
|
+
- lib/flocks/configuration.rb
|
84
|
+
- lib/flocks/ordering.rb
|
85
|
+
- lib/flocks/relationships.rb
|
86
|
+
- lib/flocks/search.rb
|
87
|
+
- lib/flocks/version.rb
|
88
|
+
- test/flocks/configuration_test.rb
|
89
|
+
- test/flocks/flocks_test.rb
|
90
|
+
- test/flocks/ordering_test.rb
|
91
|
+
- test/flocks/relationships_test.rb
|
92
|
+
- test/flocks/search_test.rb
|
93
|
+
- test/test_helper.rb
|
94
|
+
homepage: https://github.com/byliner/flocks
|
95
|
+
licenses:
|
96
|
+
- BSD
|
97
|
+
metadata: {}
|
98
|
+
post_install_message:
|
99
|
+
rdoc_options: []
|
100
|
+
require_paths:
|
101
|
+
- lib
|
102
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '0'
|
107
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
requirements: []
|
113
|
+
rubyforge_project:
|
114
|
+
rubygems_version: 2.2.0
|
115
|
+
signing_key:
|
116
|
+
specification_version: 4
|
117
|
+
summary: Single node social relationships using redis.
|
118
|
+
test_files:
|
119
|
+
- test/flocks/configuration_test.rb
|
120
|
+
- test/flocks/flocks_test.rb
|
121
|
+
- test/flocks/ordering_test.rb
|
122
|
+
- test/flocks/relationships_test.rb
|
123
|
+
- test/flocks/search_test.rb
|
124
|
+
- test/test_helper.rb
|