soulmate 0.0.4 → 0.0.5
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/Gemfile +1 -0
- data/Gemfile.lock +2 -0
- data/README.markdown +1 -1
- data/bin/soulmate +43 -4
- data/lib/soulmate.rb +1 -1
- data/lib/soulmate/helpers.rb +1 -1
- data/lib/soulmate/loader.rb +30 -16
- data/lib/soulmate/matcher.rb +11 -3
- data/lib/soulmate/server.rb +3 -3
- data/lib/soulmate/version.rb +1 -1
- data/soulmate.gemspec +5 -2
- data/test/test_soulmate.rb +60 -4
- metadata +38 -27
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -6,6 +6,7 @@ GEM
|
|
6
6
|
bundler (~> 1.0.0)
|
7
7
|
git (>= 1.2.5)
|
8
8
|
rake
|
9
|
+
multi_json (1.0.3)
|
9
10
|
rack (1.2.2)
|
10
11
|
rack-contrib (1.1.0)
|
11
12
|
rack (>= 0.9.1)
|
@@ -26,6 +27,7 @@ PLATFORMS
|
|
26
27
|
DEPENDENCIES
|
27
28
|
bundler (~> 1.0.0)
|
28
29
|
jeweler (~> 1.5.2)
|
30
|
+
multi_json (~> 1.0.3)
|
29
31
|
rack-contrib
|
30
32
|
rcov
|
31
33
|
redis (~> 2.1.1)
|
data/README.markdown
CHANGED
@@ -56,7 +56,7 @@ Once it's loaded, we can query this data by starting `soulmate-web`:
|
|
56
56
|
|
57
57
|
$ soulmate-web --foreground --no-launch --redis=redis://localhost:6379/0
|
58
58
|
|
59
|
-
And viewing the service in your browser:
|
59
|
+
And viewing the service in your browser: http://localhost:5678/search?types[]=venue&term=stad. You should see something like:
|
60
60
|
|
61
61
|
{
|
62
62
|
"term": "stad",
|
data/bin/soulmate
CHANGED
@@ -27,14 +27,47 @@ parser = OptionParser.new do |opts|
|
|
27
27
|
|
28
28
|
opts.separator ""
|
29
29
|
opts.separator "Commands:"
|
30
|
-
opts.separator " load
|
30
|
+
opts.separator " load TYPE Replaces collection specified by TYPE with items read from stdin in the JSON lines format."
|
31
|
+
opts.separator " add TYPE Adds items to collection specified by TYPE read from stdin in the JSON lines format."
|
32
|
+
opts.separator " remove TYPE Removes items from collection specified by TYPE read from stdin in the JSON lines format. Items only require an 'id', all other fields are ignored."
|
33
|
+
opts.separator " query TYPE QUERY Queries for items from collection specified by TYPE."
|
31
34
|
end
|
32
35
|
|
33
36
|
def load(type)
|
34
37
|
puts "Loading items of type #{type}..."
|
35
|
-
items = $stdin.read.split("\n").map { |l|
|
36
|
-
|
37
|
-
puts "Loaded a total of #{
|
38
|
+
items = $stdin.read.split("\n").map { |l| MultiJson.decode(l) }
|
39
|
+
loaded = Soulmate::Loader.new(type).load(items)
|
40
|
+
puts "Loaded a total of #{loaded.size} items"
|
41
|
+
end
|
42
|
+
|
43
|
+
def add(type)
|
44
|
+
puts "Adding items of type #{type}..."
|
45
|
+
loader = Soulmate::Loader.new(type)
|
46
|
+
items = $stdin.read.split("\n").map { |l| MultiJson.decode(l) }
|
47
|
+
items.each do |item|
|
48
|
+
loader.add(item)
|
49
|
+
end
|
50
|
+
puts "Loaded a total of #{items.size} items"
|
51
|
+
end
|
52
|
+
|
53
|
+
def remove(type)
|
54
|
+
puts "Removing items of type #{type}..."
|
55
|
+
loader = Soulmate::Loader.new(type)
|
56
|
+
items = $stdin.read.split("\n").map { |l| MultiJson.decode(l) }
|
57
|
+
items.each do |item|
|
58
|
+
loader.remove(item)
|
59
|
+
end
|
60
|
+
puts "Removed a total of #{items.size} items"
|
61
|
+
end
|
62
|
+
|
63
|
+
def query(type, query)
|
64
|
+
puts "> Querying '#{type}' for '#{query}'"
|
65
|
+
matcher = Soulmate::Matcher.new(type)
|
66
|
+
results = matcher.matches_for_term(query, :limit => 0)
|
67
|
+
results.each do |item|
|
68
|
+
puts MultiJson.encode(item)
|
69
|
+
end
|
70
|
+
puts "> Found #{results.size} matches"
|
38
71
|
end
|
39
72
|
|
40
73
|
parser.parse!
|
@@ -42,6 +75,12 @@ parser.parse!
|
|
42
75
|
case ARGV[0]
|
43
76
|
when 'load'
|
44
77
|
load ARGV[1]
|
78
|
+
when 'add'
|
79
|
+
add ARGV[1]
|
80
|
+
when 'remove'
|
81
|
+
remove ARGV[1]
|
82
|
+
when 'query'
|
83
|
+
query ARGV[1], ARGV[2]
|
45
84
|
else
|
46
85
|
puts parser.help
|
47
86
|
end
|
data/lib/soulmate.rb
CHANGED
data/lib/soulmate/helpers.rb
CHANGED
data/lib/soulmate/loader.rb
CHANGED
@@ -19,27 +19,41 @@ module Soulmate
|
|
19
19
|
# delete the data stored for this type
|
20
20
|
Soulmate.redis.del(database)
|
21
21
|
|
22
|
-
items_loaded = 0
|
23
22
|
items.each_with_index do |item, i|
|
24
|
-
|
25
|
-
|
26
|
-
|
23
|
+
add(item, :skip_duplicate_check => true)
|
24
|
+
end
|
25
|
+
end
|
27
26
|
|
28
|
-
|
29
|
-
|
30
|
-
|
27
|
+
# "id", "term", "score", "aliases", "data"
|
28
|
+
def add(item, opts = {})
|
29
|
+
opts = { :skip_duplicate_check => false }.merge(opts)
|
30
|
+
raise ArgumentError unless item["id"] && item["term"]
|
31
|
+
|
32
|
+
# kill any old items with this id
|
33
|
+
remove("id" => item["id"]) unless opts[:skip_duplicate_check]
|
34
|
+
|
35
|
+
# store the raw data in a separate key to reduce memory usage
|
36
|
+
Soulmate.redis.hset(database, item["id"], MultiJson.encode(item))
|
37
|
+
phrase = ([item["term"]] + (item["aliases"] || [])).join(' ')
|
38
|
+
prefixes_for_phrase(phrase).each do |p|
|
39
|
+
Soulmate.redis.sadd(base, p) # remember this prefix in a master set
|
40
|
+
Soulmate.redis.zadd("#{base}:#{p}", item["score"], item["id"]) # store the id of this term in the index
|
41
|
+
end
|
42
|
+
end
|
31
43
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
44
|
+
# remove only cares about an item's id, but for consistency takes an object
|
45
|
+
def remove(item)
|
46
|
+
prev_item = Soulmate.redis.hget(database, item["id"])
|
47
|
+
if prev_item
|
48
|
+
prev_item = MultiJson.decode(prev_item)
|
49
|
+
# undo the operations done in add
|
50
|
+
Soulmate.redis.hdel(database, prev_item["id"])
|
51
|
+
phrase = ([prev_item["term"]] + (prev_item["aliases"] || [])).join(' ')
|
52
|
+
prefixes_for_phrase(phrase).each do |p|
|
53
|
+
Soulmate.redis.srem(base, p)
|
54
|
+
Soulmate.redis.zrem("#{base}:#{p}", prev_item["id"])
|
38
55
|
end
|
39
|
-
puts "added #{i} entries" if i % 100 == 0 and i != 0
|
40
56
|
end
|
41
|
-
|
42
|
-
items_loaded
|
43
57
|
end
|
44
58
|
end
|
45
59
|
end
|
data/lib/soulmate/matcher.rb
CHANGED
@@ -3,22 +3,30 @@ module Soulmate
|
|
3
3
|
class Matcher < Base
|
4
4
|
|
5
5
|
def matches_for_term(term, options = {})
|
6
|
+
options = { :limit => 5, :cache => true }.merge(options)
|
7
|
+
|
6
8
|
words = normalize(term).split(' ').reject do |w|
|
7
9
|
w.size < MIN_COMPLETE or STOP_WORDS.include?(w)
|
8
10
|
end.sort
|
9
11
|
|
10
|
-
|
12
|
+
return [] if words.empty?
|
11
13
|
|
12
14
|
cachekey = "#{cachebase}:" + words.join('|')
|
13
15
|
|
14
|
-
if !Soulmate.redis.exists(cachekey)
|
16
|
+
if !options[:cache] || !Soulmate.redis.exists(cachekey)
|
15
17
|
interkeys = words.map { |w| "#{base}:#{w}" }
|
16
18
|
Soulmate.redis.zinterstore(cachekey, interkeys)
|
17
19
|
Soulmate.redis.expire(cachekey, 10 * 60) # expire after 10 minutes
|
18
20
|
end
|
19
21
|
|
20
22
|
ids = Soulmate.redis.zrevrange(cachekey, 0, options[:limit] - 1)
|
21
|
-
ids.size > 0
|
23
|
+
if ids.size > 0
|
24
|
+
Soulmate.redis.hmget(database, *ids)
|
25
|
+
.reject{ |r| r.nil? } # handle cached results for ids which have since been deleted
|
26
|
+
.map { |r| MultiJson.decode(r) }
|
27
|
+
else
|
28
|
+
[]
|
29
|
+
end
|
22
30
|
end
|
23
31
|
end
|
24
32
|
end
|
data/lib/soulmate/server.rb
CHANGED
@@ -14,7 +14,7 @@ module Soulmate
|
|
14
14
|
end
|
15
15
|
|
16
16
|
get '/' do
|
17
|
-
|
17
|
+
MultiJson.encode({ :soulmate => Soulmate::Version::STRING, :status => "ok" })
|
18
18
|
end
|
19
19
|
|
20
20
|
get '/search' do
|
@@ -30,7 +30,7 @@ module Soulmate
|
|
30
30
|
results[type] = matcher.matches_for_term(term, :limit => limit)
|
31
31
|
end
|
32
32
|
|
33
|
-
|
33
|
+
MultiJson.encode({
|
34
34
|
:term => params[:term],
|
35
35
|
:results => results
|
36
36
|
})
|
@@ -38,7 +38,7 @@ module Soulmate
|
|
38
38
|
|
39
39
|
not_found do
|
40
40
|
content_type 'application/json', :charset => 'utf-8'
|
41
|
-
|
41
|
+
MultiJson.encode({ :error => "not found" })
|
42
42
|
end
|
43
43
|
|
44
44
|
end
|
data/lib/soulmate/version.rb
CHANGED
data/soulmate.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{soulmate}
|
8
|
-
s.version = "0.0.
|
8
|
+
s.version = "0.0.5"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Eric Waller"]
|
12
|
-
s.date = %q{2011-
|
12
|
+
s.date = %q{2011-10-12}
|
13
13
|
s.description = %q{Soulmate is a tool to help solve the common problem of developing a fast autocomplete feature. It uses Redis's sorted sets to build an index of partial words and corresponding top matches, and provides a simple sinatra app to query them. Soulmate finishes your sentences.}
|
14
14
|
s.email = %q{eric@seatgeek.com}
|
15
15
|
s.executables = ["soulmate", "soulmate-web"]
|
@@ -55,6 +55,7 @@ Gem::Specification.new do |s|
|
|
55
55
|
|
56
56
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
57
57
|
s.add_runtime_dependency(%q<redis>, ["~> 2.1.1"])
|
58
|
+
s.add_runtime_dependency(%q<multi_json>, ["~> 1.0.3"])
|
58
59
|
s.add_runtime_dependency(%q<rack-contrib>, [">= 0"])
|
59
60
|
s.add_runtime_dependency(%q<vegas>, ["~> 0.1.8"])
|
60
61
|
s.add_runtime_dependency(%q<sinatra>, ["~> 1.2.3"])
|
@@ -68,6 +69,7 @@ Gem::Specification.new do |s|
|
|
68
69
|
s.add_runtime_dependency(%q<json>, ["~> 1.4.6"])
|
69
70
|
else
|
70
71
|
s.add_dependency(%q<redis>, ["~> 2.1.1"])
|
72
|
+
s.add_dependency(%q<multi_json>, ["~> 1.0.3"])
|
71
73
|
s.add_dependency(%q<rack-contrib>, [">= 0"])
|
72
74
|
s.add_dependency(%q<vegas>, ["~> 0.1.8"])
|
73
75
|
s.add_dependency(%q<sinatra>, ["~> 1.2.3"])
|
@@ -82,6 +84,7 @@ Gem::Specification.new do |s|
|
|
82
84
|
end
|
83
85
|
else
|
84
86
|
s.add_dependency(%q<redis>, ["~> 2.1.1"])
|
87
|
+
s.add_dependency(%q<multi_json>, ["~> 1.0.3"])
|
85
88
|
s.add_dependency(%q<rack-contrib>, [">= 0"])
|
86
89
|
s.add_dependency(%q<vegas>, ["~> 0.1.8"])
|
87
90
|
s.add_dependency(%q<sinatra>, ["~> 1.2.3"])
|
data/test/test_soulmate.rb
CHANGED
@@ -5,12 +5,12 @@ class TestSoulmate < Test::Unit::TestCase
|
|
5
5
|
items = []
|
6
6
|
venues = File.open(File.expand_path(File.dirname(__FILE__)) + '/samples/venues.json', "r")
|
7
7
|
venues.each_line do |venue|
|
8
|
-
items <<
|
8
|
+
items << MultiJson.decode(venue)
|
9
9
|
end
|
10
10
|
|
11
11
|
items_loaded = Soulmate::Loader.new('venues').load(items)
|
12
12
|
|
13
|
-
assert_equal 6, items_loaded
|
13
|
+
assert_equal 6, items_loaded.size
|
14
14
|
|
15
15
|
matcher = Soulmate::Matcher.new('venues')
|
16
16
|
results = matcher.matches_for_term('stad', :limit => 5)
|
@@ -23,12 +23,12 @@ class TestSoulmate < Test::Unit::TestCase
|
|
23
23
|
items = []
|
24
24
|
venues = File.open(File.expand_path(File.dirname(__FILE__)) + '/samples/venues.json', "r")
|
25
25
|
venues.each_line do |venue|
|
26
|
-
items <<
|
26
|
+
items << MultiJson.decode(venue)
|
27
27
|
end
|
28
28
|
|
29
29
|
items_loaded = Soulmate::Loader.new('venues').load(items)
|
30
30
|
|
31
|
-
assert_equal 6, items_loaded
|
31
|
+
assert_equal 6, items_loaded.size
|
32
32
|
|
33
33
|
matcher = Soulmate::Matcher.new('venues')
|
34
34
|
results = matcher.matches_for_term('land shark stadium', :limit => 5)
|
@@ -42,4 +42,60 @@ class TestSoulmate < Test::Unit::TestCase
|
|
42
42
|
results = matcher.matches_for_term('stadium', :limit => 5)
|
43
43
|
assert_equal 5, results.size
|
44
44
|
end
|
45
|
+
|
46
|
+
def test_can_remove_items
|
47
|
+
|
48
|
+
loader = Soulmate::Loader.new('venues')
|
49
|
+
matcher = Soulmate::Matcher.new('venues')
|
50
|
+
|
51
|
+
# empty the collection
|
52
|
+
loader.load([])
|
53
|
+
results = matcher.matches_for_term("te", :cache => false)
|
54
|
+
assert_equal 0, results.size
|
55
|
+
|
56
|
+
loader.add("id" => 1, "term" => "Testing this", "score" => 10)
|
57
|
+
results = matcher.matches_for_term("te", :cache => false)
|
58
|
+
assert_equal 1, results.size
|
59
|
+
|
60
|
+
loader.remove("id" => 1)
|
61
|
+
results = matcher.matches_for_term("te", :cache => false)
|
62
|
+
assert_equal 0, results.size
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_can_update_items
|
67
|
+
|
68
|
+
loader = Soulmate::Loader.new('venues')
|
69
|
+
matcher = Soulmate::Matcher.new('venues')
|
70
|
+
|
71
|
+
# empty the collection
|
72
|
+
loader.load([])
|
73
|
+
|
74
|
+
# initial data
|
75
|
+
loader.add("id" => 1, "term" => "Testing this", "score" => 10)
|
76
|
+
loader.add("id" => 2, "term" => "Another Term", "score" => 9)
|
77
|
+
loader.add("id" => 3, "term" => "Something different", "score" => 5)
|
78
|
+
|
79
|
+
results = matcher.matches_for_term("te", :cache => false)
|
80
|
+
assert_equal 2, results.size
|
81
|
+
assert_equal "Testing this", results.first["term"]
|
82
|
+
assert_equal 10, results.first["score"]
|
83
|
+
|
84
|
+
# update id:1
|
85
|
+
loader.add("id" => 1, "term" => "Updated", "score" => 5)
|
86
|
+
|
87
|
+
results = matcher.matches_for_term("te", :cache => false)
|
88
|
+
assert_equal 1, results.size
|
89
|
+
assert_equal "Another Term", results.first["term"]
|
90
|
+
assert_equal 9, results.first["score"]
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_prefixes_for_phrase
|
95
|
+
loader = Soulmate::Loader.new('venues')
|
96
|
+
|
97
|
+
assert_equal ["te", "tes", "test", "testi", "testin", "th", "thi", "this"], loader.prefixes_for_phrase("testin' this")
|
98
|
+
assert_equal ["te", "tes", "test"], loader.prefixes_for_phrase("test test")
|
99
|
+
assert_equal ["so", "sou", "soul", "soulm", "soulma", "soulmat", "soulmate"], loader.prefixes_for_phrase("SoUlmATE")
|
100
|
+
end
|
45
101
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: soulmate
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,12 +9,12 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-
|
12
|
+
date: 2011-10-12 00:00:00.000000000 -04:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: redis
|
17
|
-
requirement: &
|
17
|
+
requirement: &2151919160 !ruby/object:Gem::Requirement
|
18
18
|
none: false
|
19
19
|
requirements:
|
20
20
|
- - ~>
|
@@ -22,10 +22,21 @@ dependencies:
|
|
22
22
|
version: 2.1.1
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
|
-
version_requirements: *
|
25
|
+
version_requirements: *2151919160
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: multi_json
|
28
|
+
requirement: &2151917560 !ruby/object:Gem::Requirement
|
29
|
+
none: false
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.0.3
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: *2151917560
|
26
37
|
- !ruby/object:Gem::Dependency
|
27
38
|
name: rack-contrib
|
28
|
-
requirement: &
|
39
|
+
requirement: &2151916600 !ruby/object:Gem::Requirement
|
29
40
|
none: false
|
30
41
|
requirements:
|
31
42
|
- - ! '>='
|
@@ -33,10 +44,10 @@ dependencies:
|
|
33
44
|
version: '0'
|
34
45
|
type: :runtime
|
35
46
|
prerelease: false
|
36
|
-
version_requirements: *
|
47
|
+
version_requirements: *2151916600
|
37
48
|
- !ruby/object:Gem::Dependency
|
38
49
|
name: vegas
|
39
|
-
requirement: &
|
50
|
+
requirement: &2151915480 !ruby/object:Gem::Requirement
|
40
51
|
none: false
|
41
52
|
requirements:
|
42
53
|
- - ~>
|
@@ -44,10 +55,10 @@ dependencies:
|
|
44
55
|
version: 0.1.8
|
45
56
|
type: :runtime
|
46
57
|
prerelease: false
|
47
|
-
version_requirements: *
|
58
|
+
version_requirements: *2151915480
|
48
59
|
- !ruby/object:Gem::Dependency
|
49
60
|
name: sinatra
|
50
|
-
requirement: &
|
61
|
+
requirement: &2151914480 !ruby/object:Gem::Requirement
|
51
62
|
none: false
|
52
63
|
requirements:
|
53
64
|
- - ~>
|
@@ -55,10 +66,10 @@ dependencies:
|
|
55
66
|
version: 1.2.3
|
56
67
|
type: :runtime
|
57
68
|
prerelease: false
|
58
|
-
version_requirements: *
|
69
|
+
version_requirements: *2151914480
|
59
70
|
- !ruby/object:Gem::Dependency
|
60
71
|
name: shoulda
|
61
|
-
requirement: &
|
72
|
+
requirement: &2151913720 !ruby/object:Gem::Requirement
|
62
73
|
none: false
|
63
74
|
requirements:
|
64
75
|
- - ! '>='
|
@@ -66,10 +77,10 @@ dependencies:
|
|
66
77
|
version: '0'
|
67
78
|
type: :development
|
68
79
|
prerelease: false
|
69
|
-
version_requirements: *
|
80
|
+
version_requirements: *2151913720
|
70
81
|
- !ruby/object:Gem::Dependency
|
71
82
|
name: bundler
|
72
|
-
requirement: &
|
83
|
+
requirement: &2151912740 !ruby/object:Gem::Requirement
|
73
84
|
none: false
|
74
85
|
requirements:
|
75
86
|
- - ~>
|
@@ -77,10 +88,10 @@ dependencies:
|
|
77
88
|
version: 1.0.0
|
78
89
|
type: :development
|
79
90
|
prerelease: false
|
80
|
-
version_requirements: *
|
91
|
+
version_requirements: *2151912740
|
81
92
|
- !ruby/object:Gem::Dependency
|
82
93
|
name: jeweler
|
83
|
-
requirement: &
|
94
|
+
requirement: &2151911840 !ruby/object:Gem::Requirement
|
84
95
|
none: false
|
85
96
|
requirements:
|
86
97
|
- - ~>
|
@@ -88,10 +99,10 @@ dependencies:
|
|
88
99
|
version: 1.5.2
|
89
100
|
type: :development
|
90
101
|
prerelease: false
|
91
|
-
version_requirements: *
|
102
|
+
version_requirements: *2151911840
|
92
103
|
- !ruby/object:Gem::Dependency
|
93
104
|
name: rcov
|
94
|
-
requirement: &
|
105
|
+
requirement: &2151910900 !ruby/object:Gem::Requirement
|
95
106
|
none: false
|
96
107
|
requirements:
|
97
108
|
- - ! '>='
|
@@ -99,10 +110,10 @@ dependencies:
|
|
99
110
|
version: '0'
|
100
111
|
type: :development
|
101
112
|
prerelease: false
|
102
|
-
version_requirements: *
|
113
|
+
version_requirements: *2151910900
|
103
114
|
- !ruby/object:Gem::Dependency
|
104
115
|
name: redis
|
105
|
-
requirement: &
|
116
|
+
requirement: &2151909380 !ruby/object:Gem::Requirement
|
106
117
|
none: false
|
107
118
|
requirements:
|
108
119
|
- - ! '>='
|
@@ -110,10 +121,10 @@ dependencies:
|
|
110
121
|
version: '2.0'
|
111
122
|
type: :runtime
|
112
123
|
prerelease: false
|
113
|
-
version_requirements: *
|
124
|
+
version_requirements: *2151909380
|
114
125
|
- !ruby/object:Gem::Dependency
|
115
126
|
name: vegas
|
116
|
-
requirement: &
|
127
|
+
requirement: &2151907400 !ruby/object:Gem::Requirement
|
117
128
|
none: false
|
118
129
|
requirements:
|
119
130
|
- - ! '>='
|
@@ -121,10 +132,10 @@ dependencies:
|
|
121
132
|
version: 0.1.0
|
122
133
|
type: :runtime
|
123
134
|
prerelease: false
|
124
|
-
version_requirements: *
|
135
|
+
version_requirements: *2151907400
|
125
136
|
- !ruby/object:Gem::Dependency
|
126
137
|
name: sinatra
|
127
|
-
requirement: &
|
138
|
+
requirement: &2151904740 !ruby/object:Gem::Requirement
|
128
139
|
none: false
|
129
140
|
requirements:
|
130
141
|
- - ! '>='
|
@@ -132,10 +143,10 @@ dependencies:
|
|
132
143
|
version: '1.0'
|
133
144
|
type: :runtime
|
134
145
|
prerelease: false
|
135
|
-
version_requirements: *
|
146
|
+
version_requirements: *2151904740
|
136
147
|
- !ruby/object:Gem::Dependency
|
137
148
|
name: json
|
138
|
-
requirement: &
|
149
|
+
requirement: &2151901600 !ruby/object:Gem::Requirement
|
139
150
|
none: false
|
140
151
|
requirements:
|
141
152
|
- - ~>
|
@@ -143,7 +154,7 @@ dependencies:
|
|
143
154
|
version: 1.4.6
|
144
155
|
type: :runtime
|
145
156
|
prerelease: false
|
146
|
-
version_requirements: *
|
157
|
+
version_requirements: *2151901600
|
147
158
|
description: Soulmate is a tool to help solve the common problem of developing a fast
|
148
159
|
autocomplete feature. It uses Redis's sorted sets to build an index of partial words
|
149
160
|
and corresponding top matches, and provides a simple sinatra app to query them.
|
@@ -194,7 +205,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
194
205
|
version: '0'
|
195
206
|
segments:
|
196
207
|
- 0
|
197
|
-
hash:
|
208
|
+
hash: -3865399172879930147
|
198
209
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
199
210
|
none: false
|
200
211
|
requirements:
|