soulmate_rails 0.3.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.rvmrc +1 -1
- data/Rakefile +1 -1
- data/lib/soulmate/base.rb +3 -3
- data/lib/soulmate/helpers.rb +1 -1
- data/lib/soulmate/matcher.rb +5 -4
- data/lib/soulmate_rails.rb +40 -0
- data/lib/soulmate_rails/model_additions.rb +58 -1
- data/lib/soulmate_rails/railtie.rb +17 -3
- data/lib/soulmate_rails/version.rb +1 -1
- data/soulmate_rails.gemspec +2 -1
- metadata +22 -5
data/.rvmrc
CHANGED
@@ -1 +1 @@
|
|
1
|
-
rvm 1.9.3-
|
1
|
+
rvm 1.9.3-p194@soulmate_rails --create
|
data/Rakefile
CHANGED
@@ -20,7 +20,7 @@ end
|
|
20
20
|
|
21
21
|
desc 'Open an irb session preloaded with this library'
|
22
22
|
task :console do
|
23
|
-
sh 'irb -rubygems -I lib:spec -r soulmate_rails.rb -r spec_helper.rb'
|
23
|
+
sh 'irb -rubygems -I lib:spec -r irb/completion -r soulmate_rails.rb -r spec_helper.rb'
|
24
24
|
end
|
25
25
|
|
26
26
|
desc 'Release the gem'
|
data/lib/soulmate/base.rb
CHANGED
@@ -9,15 +9,15 @@ module Soulmate
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def base
|
12
|
-
"soulmate-index:#{type}"
|
12
|
+
"soulmate-index:#{Soulmate.cache_namespace}_#{type}"
|
13
13
|
end
|
14
14
|
|
15
15
|
def database
|
16
|
-
"soulmate-data:#{type}"
|
16
|
+
"soulmate-data:#{Soulmate.cache_namespace}_#{type}"
|
17
17
|
end
|
18
18
|
|
19
19
|
def cachebase
|
20
|
-
"soulmate-cache:#{type}"
|
20
|
+
"soulmate-cache:#{Soulmate.cache_namespace}_#{type}"
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|
data/lib/soulmate/helpers.rb
CHANGED
data/lib/soulmate/matcher.rb
CHANGED
@@ -2,10 +2,10 @@ module Soulmate
|
|
2
2
|
class Matcher < Base
|
3
3
|
|
4
4
|
def matches_for_term(term, options = {})
|
5
|
-
options = { :limit =>
|
6
|
-
|
5
|
+
options = { :limit => Soulmate.max_results, :cache => true }.merge(options)
|
6
|
+
|
7
7
|
words = normalize(term).split(' ').reject do |w|
|
8
|
-
w.size <
|
8
|
+
w.size < Soulmate.min_complete or Soulmate.stop_words.include?(w)
|
9
9
|
end.sort
|
10
10
|
|
11
11
|
return [] if words.empty?
|
@@ -15,7 +15,7 @@ module Soulmate
|
|
15
15
|
if !options[:cache] || !Soulmate.redis.exists(cachekey)
|
16
16
|
interkeys = words.map { |w| "#{base}:#{w}" }
|
17
17
|
Soulmate.redis.zinterstore(cachekey, interkeys)
|
18
|
-
Soulmate.redis.expire(cachekey,
|
18
|
+
Soulmate.redis.expire(cachekey, Soulmate.cache_time)
|
19
19
|
end
|
20
20
|
|
21
21
|
ids = Soulmate.redis.zrevrange(cachekey, 0, options[:limit] - 1)
|
@@ -26,6 +26,7 @@ module Soulmate
|
|
26
26
|
else
|
27
27
|
[]
|
28
28
|
end
|
29
|
+
|
29
30
|
end
|
30
31
|
end
|
31
32
|
end
|
data/lib/soulmate_rails.rb
CHANGED
@@ -17,6 +17,7 @@ module Soulmate
|
|
17
17
|
extend self
|
18
18
|
|
19
19
|
MIN_COMPLETE = 2
|
20
|
+
|
20
21
|
DEFAULT_STOP_WORDS = ["vs", "at", "the"]
|
21
22
|
|
22
23
|
def redis=(server)
|
@@ -51,4 +52,43 @@ module Soulmate
|
|
51
52
|
@stop_words = Array(arr).flatten
|
52
53
|
end
|
53
54
|
|
55
|
+
def min_complete
|
56
|
+
@min_complete ||= MIN_COMPLETE
|
57
|
+
end
|
58
|
+
|
59
|
+
def min_complete=(min_len)
|
60
|
+
if min_len.is_a? Integer
|
61
|
+
@min_complete = min_len unless min_len < 1 || min_len > 5
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def cache_time
|
66
|
+
# default to 10 minutes
|
67
|
+
@cache_time ||= 10 * 60
|
68
|
+
end
|
69
|
+
|
70
|
+
def cache_time=(time_period)
|
71
|
+
if time_period.is_a? Integer
|
72
|
+
@cache_time = time_period unless time_period < 1
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def cache_namespace
|
77
|
+
@cache_namespace
|
78
|
+
end
|
79
|
+
|
80
|
+
def cache_namespace=(namespace)
|
81
|
+
@cache_namespace = namespace
|
82
|
+
end
|
83
|
+
|
84
|
+
def max_results
|
85
|
+
# default to 10 max results returned
|
86
|
+
@max_results ||= 10
|
87
|
+
end
|
88
|
+
|
89
|
+
def max_results=(max_num)
|
90
|
+
if max_num.is_a? Integer
|
91
|
+
@max_results = max_num unless max_num < 1
|
92
|
+
end
|
93
|
+
end
|
54
94
|
end
|
@@ -12,7 +12,7 @@ module SoulmateRails
|
|
12
12
|
loader = instance_variable_get("@#{self.class.name_for(attribute)}_loader") || instance_variable_set("@#{self.class.name_for(attribute)}_loader", Soulmate::Loader.new(self.class.name_for(attribute)))
|
13
13
|
item = {
|
14
14
|
'id' => "#{attribute}_#{self.id}",
|
15
|
-
'term' => send(attribute),
|
15
|
+
'term' => send(attribute).encode('UTF-8'),
|
16
16
|
'score' => ( respond_to?(options[:score]) ? send(options[:score]) : options[:score] )
|
17
17
|
}
|
18
18
|
|
@@ -59,6 +59,22 @@ module SoulmateRails
|
|
59
59
|
opts = opts.empty? ? {} : opts.first
|
60
60
|
search_by(attribute, term, opts)
|
61
61
|
end
|
62
|
+
|
63
|
+
# use only redis cache without touching backend database
|
64
|
+
define_singleton_method "search_cache_by_#{attribute}" do |term, *opts|
|
65
|
+
opts = opts.empty? ? {} : opts.first
|
66
|
+
search_cache_by(attribute, term, opts)
|
67
|
+
end
|
68
|
+
|
69
|
+
# define utility function to clean out cached data
|
70
|
+
define_singleton_method "flush_cache_by_#{attribute}" do
|
71
|
+
flush_cache(attribute)
|
72
|
+
end
|
73
|
+
|
74
|
+
# define utility function to reload data from database
|
75
|
+
define_singleton_method "reload_cache_by_#{attribute}" do |options|
|
76
|
+
reload_cache(attribute, options)
|
77
|
+
end
|
62
78
|
end
|
63
79
|
|
64
80
|
def search_by(attribute, term, options={})
|
@@ -81,6 +97,47 @@ module SoulmateRails
|
|
81
97
|
def normalized_class_name
|
82
98
|
self.name.gsub('::', '_').downcase
|
83
99
|
end
|
100
|
+
|
101
|
+
def flush_cache(attribute)
|
102
|
+
loader = instance_variable_get("@#{name_for(attribute)}") || instance_variable_set("@#{name_for(attribute)}", Soulmate::Loader.new(name_for(attribute)))
|
103
|
+
|
104
|
+
# delete the sorted sets for this type
|
105
|
+
phrases = Soulmate.redis.keys(loader.base + "*")
|
106
|
+
|
107
|
+
# delete cached sets for previous
|
108
|
+
cache_phrases = Soulmate.redis.keys(loader.cachebase + "*")
|
109
|
+
|
110
|
+
Soulmate.redis.pipelined do
|
111
|
+
phrases.each do |p|
|
112
|
+
Soulmate.redis.del(p)
|
113
|
+
end
|
114
|
+
|
115
|
+
cache_phrases.each do |p|
|
116
|
+
Soulmate.redis.del(p)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# delete the data stored for this type
|
121
|
+
Soulmate.redis.del(loader.database)
|
122
|
+
end
|
123
|
+
|
124
|
+
# example call: City.reload_cache(:name, {:score => :autocomplete_score, :data => :autocomplete_data})
|
125
|
+
# example call: City.reload_cache_by_name({:score => :autocomplete_score, :data => :autocomplete_data})
|
126
|
+
def reload_cache(attribute, options)
|
127
|
+
flush_cache(attribute)
|
128
|
+
self.find_each { |object| object.update_index_for(attribute, options) }
|
129
|
+
end
|
130
|
+
|
131
|
+
# define a mathod to return results from cache instead of connecting to backend database
|
132
|
+
def search_cache_by(attribute, term, options={})
|
133
|
+
matcher = instance_variable_get("@#{name_for(attribute)}_matcher") || instance_variable_set("@#{name_for(attribute)}_matcher", Soulmate::Matcher.new(name_for(attribute)))
|
134
|
+
matches = matcher.matches_for_term(term, options)
|
135
|
+
|
136
|
+
res = []
|
137
|
+
matches.each {|m| res << [m['id'].split('_')[-1].to_i, m['data']] }
|
138
|
+
|
139
|
+
res
|
140
|
+
end
|
84
141
|
end
|
85
142
|
end
|
86
143
|
end
|
@@ -7,9 +7,23 @@ module SoulmateRails
|
|
7
7
|
end
|
8
8
|
|
9
9
|
initializer 'soulmate_rails.set_configs' do |app|
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
# configuration file for redis server and soulmate
|
11
|
+
Soulmate.cache_namespace ||= "#{app.engine_name}_#{Rails.env}"
|
12
|
+
redis_config_file = "config/redis_server.yml"
|
13
|
+
if File.exists?(redis_config_file)
|
14
|
+
file = YAML.load_file(redis_config_file)
|
15
|
+
Soulmate.redis = file[:redis_url] if file[:redis_url]
|
16
|
+
Soulmate.cache_time = file[:cache_time] if file[:cache_time]
|
17
|
+
Soulmate.max_results = file[:max_results] if file[:max_results]
|
18
|
+
end
|
19
|
+
# configuration file for stopping words
|
20
|
+
stop_words_file = "config/stopping_words.yml"
|
21
|
+
if File.exists?(stop_words_file)
|
22
|
+
stopping_words = []
|
23
|
+
s_file = YAML.load_file(stop_words_file)
|
24
|
+
s_file.each_line { |line| stopping_words.push(line) }
|
25
|
+
Soulmate.stop_words = stopping_words
|
26
|
+
end
|
13
27
|
end
|
14
28
|
end
|
15
29
|
end
|
data/soulmate_rails.gemspec
CHANGED
@@ -13,7 +13,7 @@ Gem::Specification.new do |s|
|
|
13
13
|
s.authors = ["Dhruva Sagar"]
|
14
14
|
s.homepage = "http://github.com/dhruvasagar/soulmate_rails"
|
15
15
|
s.summary = "Redis-backed autocompletion engine for Rails"
|
16
|
-
s.description = "Soulmate Rails is a tool to help solve the common problem of developing a fast autocomplete feature for Rails. It uses Redis's sorted sets to build an index of partial words and corresponding top matches."
|
16
|
+
s.description = "Soulmate Rails is a tool to help solve the common problem of developing a fast autocomplete feature for Rails. It uses Redis's sorted sets to build an index of partial words and corresponding top matches. Modified by Jim Zhan to add utility functions and for bug fixing."
|
17
17
|
|
18
18
|
s.files = `git ls-files`.split("\n")
|
19
19
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
@@ -23,6 +23,7 @@ Gem::Specification.new do |s|
|
|
23
23
|
s.require_paths = ["lib"]
|
24
24
|
|
25
25
|
s.add_runtime_dependency 'redis', '>= 2.0'
|
26
|
+
s.add_runtime_dependency 'json', '>= 1.7.7'
|
26
27
|
s.add_runtime_dependency 'multi_json', '>= 1.0'
|
27
28
|
s.add_runtime_dependency 'activesupport', '>= 0'
|
28
29
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: soulmate_rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-06-19 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: redis
|
@@ -27,6 +27,22 @@ dependencies:
|
|
27
27
|
- - ! '>='
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: '2.0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: json
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 1.7.7
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 1.7.7
|
30
46
|
- !ruby/object:Gem::Dependency
|
31
47
|
name: multi_json
|
32
48
|
requirement: !ruby/object:Gem::Requirement
|
@@ -125,7 +141,8 @@ dependencies:
|
|
125
141
|
version: '0'
|
126
142
|
description: Soulmate Rails is a tool to help solve the common problem of developing
|
127
143
|
a fast autocomplete feature for Rails. It uses Redis's sorted sets to build an index
|
128
|
-
of partial words and corresponding top matches.
|
144
|
+
of partial words and corresponding top matches. Modified by Jim Zhan to add utility
|
145
|
+
functions and for bug fixing.
|
129
146
|
email:
|
130
147
|
- dhruva.sagar@gmail.com
|
131
148
|
executables: []
|
@@ -172,7 +189,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
172
189
|
version: '0'
|
173
190
|
segments:
|
174
191
|
- 0
|
175
|
-
hash:
|
192
|
+
hash: 1304314171883105113
|
176
193
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
177
194
|
none: false
|
178
195
|
requirements:
|
@@ -181,7 +198,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
181
198
|
version: '0'
|
182
199
|
segments:
|
183
200
|
- 0
|
184
|
-
hash:
|
201
|
+
hash: 1304314171883105113
|
185
202
|
requirements: []
|
186
203
|
rubyforge_project:
|
187
204
|
rubygems_version: 1.8.23
|