acts_as_indexed 0.7.8 → 0.8.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/.travis.yml +13 -0
- data/CHANGELOG +12 -1
- data/Gemfile +3 -2
- data/Gemfile.lock +21 -10
- data/MIT-LICENSE +1 -1
- data/README.rdoc +29 -32
- data/Rakefile +16 -11
- data/VERSION +1 -1
- data/acts_as_indexed.gemspec +16 -4
- data/gemfiles/rails2_3.gemfile +8 -0
- data/gemfiles/rails3_0.gemfile +7 -0
- data/gemfiles/rails3_1.gemfile +7 -0
- data/gemfiles/rails3_2.gemfile +7 -0
- data/lib/acts_as_indexed.rb +8 -6
- data/lib/acts_as_indexed/class_methods.rb +41 -23
- data/lib/acts_as_indexed/configuration.rb +10 -5
- data/lib/acts_as_indexed/instance_methods.rb +2 -7
- data/lib/acts_as_indexed/pre_tokenizer.rb +11 -0
- data/lib/acts_as_indexed/search_atom.rb +3 -8
- data/lib/acts_as_indexed/search_index.rb +27 -24
- data/lib/acts_as_indexed/singleton_methods.rb +1 -6
- data/lib/acts_as_indexed/storage.rb +31 -29
- data/lib/acts_as_indexed/token_normalizer.rb +21 -0
- data/lib/acts_as_indexed/tokenizer.rb +10 -0
- data/lib/will_paginate_search.rb +0 -4
- data/test/abstract_unit.rb +7 -1
- data/test/acts_as_indexed_test.rb +26 -14
- data/test/configuration_test.rb +9 -0
- data/test/pre_tokenizer_test.rb +10 -0
- data/test/token_normalizer_test.rb +26 -0
- data/test/tokenizer_test.rb +14 -0
- data/test/will_paginate_search_test.rb +26 -0
- metadata +15 -3
data/.travis.yml
ADDED
data/CHANGELOG
CHANGED
@@ -1,5 +1,16 @@
|
|
1
|
+
===0.8.0
|
2
|
+
- Fixed bug where intentianal hyphenation was treated as a negative query. Fixes #31.
|
3
|
+
- Fixed bug where will_paginate_search was not being required. Fixes #23.
|
4
|
+
- Fixed bug where quoted phrases were matched across field boundaries. [novalis - David Turner]
|
5
|
+
- Fixed bug where records with indentical match-rankings were returned in different orders under different ruby implementations.
|
6
|
+
- Storage is now process and thread-safe. Fixes issue #34. [rsamoilov - Roman Samoilov]
|
7
|
+
- Added configuration option to force is-Windows mode for storage. Fixes issues #32, #39.
|
8
|
+
- Added multiple Gemfiles for TravisCI. https://travis-ci.org/dougal/acts_as_indexed
|
9
|
+
- Acts as Indexed can now be tested stand-alone without a generated Rails app.
|
10
|
+
- ModelKlass.build_index is now a public method
|
11
|
+
|
1
12
|
===0.7.8 [14 March 2011]
|
2
|
-
- Fixed bug with file renaming on Windows. Fixes issue #21. [
|
13
|
+
- Fixed bug with file renaming on Windows. Fixes issue #21. [gabynamiman - Gabriel Namiman]
|
3
14
|
|
4
15
|
===0.7.7 [14 November 2011]
|
5
16
|
- Fixed bug with out-of-date indexes on Windows. Fixes issue #20. [parndt - Philip Arndt]
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,24 +1,35 @@
|
|
1
1
|
GEM
|
2
2
|
remote: http://rubygems.org/
|
3
3
|
specs:
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
4
|
+
activemodel (3.2.8)
|
5
|
+
activesupport (= 3.2.8)
|
6
|
+
builder (~> 3.0.0)
|
7
|
+
activerecord (3.2.8)
|
8
|
+
activemodel (= 3.2.8)
|
9
|
+
activesupport (= 3.2.8)
|
10
|
+
arel (~> 3.0.2)
|
11
|
+
tzinfo (~> 0.3.29)
|
12
|
+
activesupport (3.2.8)
|
13
|
+
i18n (~> 0.6)
|
14
|
+
multi_json (~> 1.0)
|
15
|
+
arel (3.0.2)
|
16
|
+
builder (3.0.4)
|
17
|
+
i18n (0.6.1)
|
9
18
|
mocha (0.9.11)
|
10
19
|
rake
|
20
|
+
multi_json (1.3.7)
|
11
21
|
rake (0.8.7)
|
12
22
|
rcov (0.9.9)
|
13
|
-
sqlite3 (1.3.
|
14
|
-
|
15
|
-
|
23
|
+
sqlite3 (1.3.6)
|
24
|
+
tzinfo (0.3.33)
|
25
|
+
will_paginate (3.0.3)
|
16
26
|
|
17
27
|
PLATFORMS
|
18
28
|
ruby
|
19
29
|
|
20
30
|
DEPENDENCIES
|
21
|
-
|
31
|
+
activerecord
|
22
32
|
mocha
|
23
33
|
rcov
|
24
|
-
sqlite3
|
34
|
+
sqlite3 (~> 1.3.5)
|
35
|
+
will_paginate (~> 3.0.3)
|
data/MIT-LICENSE
CHANGED
data/README.rdoc
CHANGED
@@ -16,30 +16,23 @@ app with no dependencies and minimal setup.
|
|
16
16
|
|
17
17
|
== Resources
|
18
18
|
|
19
|
-
===
|
19
|
+
=== Installation
|
20
20
|
|
21
|
-
====
|
22
|
-
./script/plugin install git://github.com/dougal/acts_as_indexed.git
|
23
|
-
|
24
|
-
==== Rails 3.x.x
|
25
|
-
rails plugin install git://github.com/dougal/acts_as_indexed.git
|
26
|
-
|
27
|
-
==== As a Gem
|
28
|
-
Despite this being slightly against the the original ethos of the project,
|
29
|
-
acts_as_indexed is now available as a Gem as several people have requested it.
|
21
|
+
==== Add to your gemfile
|
30
22
|
|
31
23
|
gem install acts_as_indexed
|
32
24
|
|
33
|
-
|
34
|
-
Gemfile (Rails 3.x.x).
|
25
|
+
Run <tt>bundle install</tt>. Done.
|
35
26
|
|
36
|
-
====
|
27
|
+
==== Still on Rails 2.x.x without Bundler?
|
28
|
+
|
29
|
+
./script/plugin install git://github.com/dougal/acts_as_indexed.git
|
37
30
|
|
38
31
|
If you don't have git installed, but still want the plugin, you can download
|
39
32
|
the plugin from the GitHub page (http://github.com/dougal/acts_as_indexed) and
|
40
33
|
unpack it into the <tt>vendor/plugins</tt> directory of your rails app.
|
41
34
|
|
42
|
-
===
|
35
|
+
=== Upgrading
|
43
36
|
|
44
37
|
When upgrading to a new version of acts_as_indexed it is recommended you
|
45
38
|
delete the index directory and allow it to be rebuilt.
|
@@ -121,18 +114,18 @@ scope on your model, passing a query as an argument.
|
|
121
114
|
|
122
115
|
The following query operators are supported:
|
123
116
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
117
|
+
AND :: This is the default option. 'cat dog' will find records matching 'cat' AND 'dog'.
|
118
|
+
NOT :: 'cat -dog' will find records matching 'cat' AND NOT 'dog'
|
119
|
+
INCLUDE :: 'cat +me' will find records matching 'cat' and 'me', even if 'me' is smaller than the +min_word_size+
|
120
|
+
"" :: Quoted terms are matched as phrases. '"cat dog"' will find records matching the whole phrase. Quoted terms can be preceded by the NOT operator; 'cat -"big dog"' etc. Quoted terms can include words shorter than the +min_word_size+.
|
121
|
+
^ :: Terms that begin with ^ will match records that contain a word starting with the term. '^cat' will find matches containing 'cat', 'catapult', 'caterpillar' etc.
|
122
|
+
^"" :: A quoted term that begins with ^ matches any phrase that begin with this phrase. '^"cat d"' will find records matching the whole phrases "cat dog" and "cat dinner". This type of search is useful for autocomplete inputs.
|
130
123
|
|
131
124
|
=== Pagination
|
132
125
|
|
133
126
|
==== With Relevance
|
134
127
|
|
135
|
-
Pagination is supported via the +paginate_search+ method whose first argument is the search query, followed all the standard will_paginate arguments.
|
128
|
+
Pagination is supported via the +paginate_search+ method whose first argument is the search query, followed by all the standard will_paginate arguments.
|
136
129
|
|
137
130
|
@images = Image.paginate_search('girl', :page => 1, :per_page => 5)
|
138
131
|
|
@@ -160,14 +153,12 @@ A full rundown of the available configuration options can be found in
|
|
160
153
|
|
161
154
|
Acts As Indexed supports Heroku out-of-the-box. The index is created in the
|
162
155
|
tmp directory, which is the only writeable part of the Heroku dyno filesystem.
|
156
|
+
Please read Heroku's documentation(
|
157
|
+
https://devcenter.heroku.com/articles/read-only-filesystem) regarding their file-system.
|
163
158
|
|
164
159
|
== RDoc Documentation
|
165
160
|
|
166
|
-
|
167
|
-
acts_as_indexed plugin folder. Then point your web browser at
|
168
|
-
<tt>vendor/plugins/acts_as_indexed/rdoc/index.html</tt>.
|
169
|
-
|
170
|
-
Alternatively, you can view the rdoc documentation
|
161
|
+
View the rdoc documentation
|
171
162
|
online[http://rdoc.info/projects/dougal/acts_as_indexed/].
|
172
163
|
|
173
164
|
|
@@ -178,6 +169,9 @@ All of the above are most welcome. mailto:dougal.s@gmail.com
|
|
178
169
|
|
179
170
|
== Contributors
|
180
171
|
|
172
|
+
A huge thanks to all the contributors to this library. Without them many
|
173
|
+
bugfixes and features wouldn't have happened.
|
174
|
+
|
181
175
|
* Douglas F Shearer - http://douglasfshearer.com
|
182
176
|
* Thomas Pomfret
|
183
177
|
* Philip Arndt
|
@@ -189,13 +183,16 @@ All of the above are most welcome. mailto:dougal.s@gmail.com
|
|
189
183
|
* Ben Anderson
|
190
184
|
* Theron Toomey
|
191
185
|
* Uģis Ozols
|
186
|
+
* Gabriel Namiman
|
187
|
+
* Roman Samoilov
|
188
|
+
* David Turner
|
192
189
|
|
193
190
|
|
194
|
-
==
|
191
|
+
== Unicode (UTF8) Support
|
192
|
+
|
193
|
+
At the moment acts_as_indexed only works with Unicode characters when used in
|
194
|
+
the following way:
|
195
195
|
|
196
|
-
Future releases will be looking to add the following features:
|
197
|
-
* Optional html scrubbing during indexing.
|
198
|
-
* Ranking affected by field weightings.
|
199
|
-
* Support for DataMapper, Sequel and the various MongoDB ORMs.
|
200
|
-
* UTF-8 support. See the current solution in the following Gist:
|
201
196
|
https://gist.github.com/193903bb4e0d6e5debe1
|
197
|
+
|
198
|
+
I have rewritten the tokenization process to allow easier handling of this in the future.
|
data/Rakefile
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'rake'
|
2
2
|
require 'rake/testtask'
|
3
|
-
require 'rdoc/task'
|
4
3
|
|
5
4
|
desc 'Default: run unit tests.'
|
6
5
|
task :default => :test
|
@@ -13,16 +12,6 @@ Rake::TestTask.new(:test) do |t|
|
|
13
12
|
t.verbose = true
|
14
13
|
end
|
15
14
|
|
16
|
-
desc 'Generate documentation for the acts_as_indexed plugin.'
|
17
|
-
Rake::RDocTask.new(:rdoc) do |rdoc|
|
18
|
-
rdoc.rdoc_dir = 'rdoc'
|
19
|
-
rdoc.title = 'ActsAsIndexed'
|
20
|
-
rdoc.options << '--line-numbers' << '--inline-source'
|
21
|
-
rdoc.rdoc_files.include('README.rdoc')
|
22
|
-
rdoc.rdoc_files.include('CHANGELOG')
|
23
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
24
|
-
end
|
25
|
-
|
26
15
|
namespace :rcov do
|
27
16
|
desc "Generate a coverage report in coverage/"
|
28
17
|
task :gen do
|
@@ -35,6 +24,22 @@ namespace :rcov do
|
|
35
24
|
end
|
36
25
|
end
|
37
26
|
|
27
|
+
begin
|
28
|
+
require 'sdoc'
|
29
|
+
|
30
|
+
desc 'Generate documentation for the acts_as_indexed plugin.'
|
31
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
32
|
+
rdoc.rdoc_dir = 'rdoc'
|
33
|
+
rdoc.title = 'ActsAsIndexed'
|
34
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
35
|
+
rdoc.rdoc_files.include('README.rdoc')
|
36
|
+
rdoc.rdoc_files.include('CHANGELOG')
|
37
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
38
|
+
end
|
39
|
+
rescue LoadError
|
40
|
+
puts "sdoc not installed"
|
41
|
+
end
|
42
|
+
|
38
43
|
begin
|
39
44
|
require 'jeweler'
|
40
45
|
Jeweler::Tasks.new do |gemspec|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.8.0
|
data/acts_as_indexed.gemspec
CHANGED
@@ -5,17 +5,18 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "acts_as_indexed"
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.8.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Douglas F Shearer"]
|
12
|
-
s.date = "2012-
|
12
|
+
s.date = "2012-12-20"
|
13
13
|
s.description = "Acts As Indexed is a plugin which provides a pain-free way to add fulltext search to your Ruby on Rails app"
|
14
14
|
s.email = "dougal.s@gmail.com"
|
15
15
|
s.extra_rdoc_files = [
|
16
16
|
"README.rdoc"
|
17
17
|
]
|
18
18
|
s.files = [
|
19
|
+
".travis.yml",
|
19
20
|
"CHANGELOG",
|
20
21
|
"Gemfile",
|
21
22
|
"Gemfile.lock",
|
@@ -24,14 +25,21 @@ Gem::Specification.new do |s|
|
|
24
25
|
"Rakefile",
|
25
26
|
"VERSION",
|
26
27
|
"acts_as_indexed.gemspec",
|
28
|
+
"gemfiles/rails2_3.gemfile",
|
29
|
+
"gemfiles/rails3_0.gemfile",
|
30
|
+
"gemfiles/rails3_1.gemfile",
|
31
|
+
"gemfiles/rails3_2.gemfile",
|
27
32
|
"lib/acts_as_indexed.rb",
|
28
33
|
"lib/acts_as_indexed/class_methods.rb",
|
29
34
|
"lib/acts_as_indexed/configuration.rb",
|
30
35
|
"lib/acts_as_indexed/instance_methods.rb",
|
36
|
+
"lib/acts_as_indexed/pre_tokenizer.rb",
|
31
37
|
"lib/acts_as_indexed/search_atom.rb",
|
32
38
|
"lib/acts_as_indexed/search_index.rb",
|
33
39
|
"lib/acts_as_indexed/singleton_methods.rb",
|
34
40
|
"lib/acts_as_indexed/storage.rb",
|
41
|
+
"lib/acts_as_indexed/token_normalizer.rb",
|
42
|
+
"lib/acts_as_indexed/tokenizer.rb",
|
35
43
|
"lib/will_paginate_search.rb",
|
36
44
|
"rails/init.rb",
|
37
45
|
"test/abstract_unit.rb",
|
@@ -40,13 +48,17 @@ Gem::Specification.new do |s|
|
|
40
48
|
"test/database.yml",
|
41
49
|
"test/fixtures/post.rb",
|
42
50
|
"test/fixtures/posts.yml",
|
51
|
+
"test/pre_tokenizer_test.rb",
|
43
52
|
"test/schema.rb",
|
44
53
|
"test/search_atom_test.rb",
|
45
|
-
"test/search_index_test.rb"
|
54
|
+
"test/search_index_test.rb",
|
55
|
+
"test/token_normalizer_test.rb",
|
56
|
+
"test/tokenizer_test.rb",
|
57
|
+
"test/will_paginate_search_test.rb"
|
46
58
|
]
|
47
59
|
s.homepage = "http://github.com/dougal/acts_as_indexed"
|
48
60
|
s.require_paths = ["lib"]
|
49
|
-
s.rubygems_version = "1.8.
|
61
|
+
s.rubygems_version = "1.8.23"
|
50
62
|
s.summary = "Acts As Indexed is a plugin which provides a pain-free way to add fulltext search to your Ruby on Rails app"
|
51
63
|
|
52
64
|
if s.respond_to? :specification_version then
|
data/lib/acts_as_indexed.rb
CHANGED
@@ -1,8 +1,3 @@
|
|
1
|
-
# ActsAsIndexed
|
2
|
-
# Copyright (c) 2007 - 2011 Douglas F Shearer.
|
3
|
-
# http://douglasfshearer.com
|
4
|
-
# Distributed under the MIT license as included with this plugin.
|
5
|
-
|
6
1
|
require 'active_record'
|
7
2
|
|
8
3
|
require 'acts_as_indexed/class_methods'
|
@@ -12,6 +7,13 @@ require 'acts_as_indexed/configuration'
|
|
12
7
|
require 'acts_as_indexed/search_index'
|
13
8
|
require 'acts_as_indexed/search_atom'
|
14
9
|
require 'acts_as_indexed/storage'
|
10
|
+
require 'acts_as_indexed/pre_tokenizer'
|
11
|
+
require 'acts_as_indexed/tokenizer'
|
12
|
+
require 'acts_as_indexed/token_normalizer'
|
13
|
+
|
14
|
+
if defined?(WillPaginate)
|
15
|
+
require 'will_paginate_search'
|
16
|
+
end
|
15
17
|
|
16
18
|
module ActsAsIndexed #:nodoc:
|
17
19
|
|
@@ -31,7 +33,7 @@ module ActsAsIndexed #:nodoc:
|
|
31
33
|
|
32
34
|
# Call this method to modify defaults in your initializers.
|
33
35
|
#
|
34
|
-
# Example showing defaults:
|
36
|
+
# Example showing some defaults:
|
35
37
|
# ActsAsIndexed.configure do |config|
|
36
38
|
# config.index_file = [Rails.root,'index']
|
37
39
|
# config.index_file_depth = 3
|
@@ -1,8 +1,3 @@
|
|
1
|
-
# ActsAsIndexed
|
2
|
-
# Copyright (c) 2007 - 2011 Douglas F Shearer.
|
3
|
-
# http://douglasfshearer.com
|
4
|
-
# Distributed under the MIT license as included with this plugin.
|
5
|
-
|
6
1
|
module ActsAsIndexed
|
7
2
|
|
8
3
|
module ClassMethods
|
@@ -59,7 +54,7 @@ module ActsAsIndexed
|
|
59
54
|
def index_add(record)
|
60
55
|
return if self.aai_config.disable_auto_indexing
|
61
56
|
|
62
|
-
build_index
|
57
|
+
build_index
|
63
58
|
index = new_index
|
64
59
|
index.add_record(record)
|
65
60
|
@query_cache = {}
|
@@ -82,7 +77,7 @@ module ActsAsIndexed
|
|
82
77
|
def index_update(record)
|
83
78
|
return if self.aai_config.disable_auto_indexing
|
84
79
|
|
85
|
-
build_index
|
80
|
+
build_index
|
86
81
|
index = new_index
|
87
82
|
index.update_record(record,find(record.id))
|
88
83
|
@query_cache = {}
|
@@ -110,7 +105,7 @@ module ActsAsIndexed
|
|
110
105
|
|
111
106
|
# Run the query if not already in cache.
|
112
107
|
if !@query_cache || !@query_cache[query]
|
113
|
-
build_index
|
108
|
+
build_index
|
114
109
|
(@query_cache ||= {})[query] = new_index.search(query)
|
115
110
|
end
|
116
111
|
|
@@ -129,7 +124,7 @@ module ActsAsIndexed
|
|
129
124
|
# slice up the results by offset and limit
|
130
125
|
offset = find_options[:offset] || 0
|
131
126
|
limit = find_options.include?(:limit) ? find_options[:limit] : @query_cache[query].size
|
132
|
-
part_query = @query_cache[query].
|
127
|
+
part_query = sort(@query_cache[query]).slice(offset,limit).map{ |r| r.first }
|
133
128
|
|
134
129
|
# Set these to nil as we are dealing with the pagination by setting
|
135
130
|
# exactly what records we want.
|
@@ -147,33 +142,56 @@ module ActsAsIndexed
|
|
147
142
|
if find_options.include?(:order)
|
148
143
|
records # Just return the records without ranking them.
|
149
144
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
145
|
+
else
|
146
|
+
# Results come back in random order from SQL, so order again.
|
147
|
+
ranked_records = ActiveSupport::OrderedHash.new
|
148
|
+
records.each do |r|
|
149
|
+
ranked_records[r] = @query_cache[query][r.id]
|
150
|
+
end
|
156
151
|
|
157
|
-
|
158
|
-
|
152
|
+
sort(ranked_records.to_a).map{ |r| r.first }
|
153
|
+
end
|
159
154
|
end
|
160
155
|
|
161
156
|
end
|
162
157
|
|
163
|
-
private
|
164
|
-
|
165
|
-
def new_index
|
166
|
-
SearchIndex.new(aai_fields, aai_config)
|
167
|
-
end
|
168
|
-
|
169
158
|
# Builds an index from scratch for the current model class.
|
159
|
+
# Does not run if the index already exists.
|
160
|
+
|
170
161
|
def build_index
|
162
|
+
return if aai_config.index_file.directory?
|
163
|
+
|
171
164
|
index = new_index
|
172
165
|
find_in_batches({ :batch_size => 500 }) do |records|
|
173
166
|
index.add_records(records)
|
174
167
|
end
|
175
168
|
end
|
176
169
|
|
170
|
+
private
|
171
|
+
|
172
|
+
# If two records or record IDs have the same rank, sort them by ID.
|
173
|
+
# This prevents a different order being returned by different Rubies.
|
174
|
+
def sort(ranked_records)
|
175
|
+
ranked_records.sort { |a, b|
|
176
|
+
a_score = a.last
|
177
|
+
a_id = a.first.is_a?(Fixnum) ? a.first : a.first.id
|
178
|
+
|
179
|
+
b_score = b.last
|
180
|
+
b_id = b.first.is_a?(Fixnum) ? b.first : b.first.id
|
181
|
+
|
182
|
+
if a_score == b_score
|
183
|
+
a_id <=> b_id
|
184
|
+
else
|
185
|
+
a_score <=> b_score
|
186
|
+
end
|
187
|
+
|
188
|
+
}
|
189
|
+
end
|
190
|
+
|
191
|
+
def new_index
|
192
|
+
SearchIndex.new(aai_fields, aai_config)
|
193
|
+
end
|
194
|
+
|
177
195
|
end
|
178
196
|
|
179
197
|
end
|
@@ -1,8 +1,3 @@
|
|
1
|
-
# ActsAsIndexed
|
2
|
-
# Copyright (c) 2007 - 2011 Douglas F Shearer.
|
3
|
-
# http://douglasfshearer.com
|
4
|
-
# Distributed under the MIT license as included with this plugin.
|
5
|
-
|
6
1
|
module ActsAsIndexed
|
7
2
|
# Used to set up and modify settings for acts_as_indexed.
|
8
3
|
class Configuration
|
@@ -35,6 +30,11 @@ module ActsAsIndexed
|
|
35
30
|
# Default is false.
|
36
31
|
attr_accessor :disable_auto_indexing
|
37
32
|
|
33
|
+
# Disable advanced features not compatible with the Windows filesystem.
|
34
|
+
# Set to true to disable.
|
35
|
+
# Default is guessed depending on current platform.
|
36
|
+
attr_writer :is_windows_filesystem
|
37
|
+
|
38
38
|
def initialize
|
39
39
|
@index_file = nil
|
40
40
|
@index_file_depth = 3
|
@@ -42,6 +42,7 @@ module ActsAsIndexed
|
|
42
42
|
@if_proc = if_proc
|
43
43
|
@case_sensitive = false
|
44
44
|
@disable_auto_indexing = false
|
45
|
+
@is_windows_filesystem = RUBY_PLATFORM[/mswin32|mingw|cygwin/]
|
45
46
|
end
|
46
47
|
|
47
48
|
# Since we cannot expect Rails to be available on load, it is best to put
|
@@ -74,5 +75,9 @@ module ActsAsIndexed
|
|
74
75
|
@if_proc ||= Proc.new{true}
|
75
76
|
end
|
76
77
|
|
78
|
+
def is_windows_filesystem?
|
79
|
+
!!@is_windows_filesystem
|
80
|
+
end
|
81
|
+
|
77
82
|
end
|
78
83
|
end
|
@@ -1,10 +1,5 @@
|
|
1
|
-
# ActsAsIndexed
|
2
|
-
# Copyright (c) 2007 - 2011 Douglas F Shearer.
|
3
|
-
# http://douglasfshearer.com
|
4
|
-
# Distributed under the MIT license as included with this plugin.
|
5
|
-
|
6
1
|
module ActsAsIndexed
|
7
|
-
|
2
|
+
|
8
3
|
# Adds model class instance methods.
|
9
4
|
# Methods are called automatically by ActiveRecord on +save+, +destroy+,
|
10
5
|
# and +update+ of model instances.
|
@@ -28,5 +23,5 @@ module ActsAsIndexed
|
|
28
23
|
self.class.index_update(self)
|
29
24
|
end
|
30
25
|
end
|
31
|
-
|
26
|
+
|
32
27
|
end
|
@@ -1,8 +1,3 @@
|
|
1
|
-
# ActsAsIndexed
|
2
|
-
# Copyright (c) 2007 - 2011 Douglas F Shearer.
|
3
|
-
# http://douglasfshearer.com
|
4
|
-
# Distributed under the MIT license as included with this plugin.
|
5
|
-
|
6
1
|
module ActsAsIndexed #:nodoc:
|
7
2
|
class SearchAtom
|
8
3
|
|
@@ -16,7 +11,7 @@ module ActsAsIndexed #:nodoc:
|
|
16
11
|
|
17
12
|
attr_reader :records
|
18
13
|
|
19
|
-
def initialize(records=
|
14
|
+
def initialize(records=ActiveSupport::OrderedHash.new)
|
20
15
|
@records = records
|
21
16
|
end
|
22
17
|
|
@@ -68,7 +63,7 @@ module ActsAsIndexed #:nodoc:
|
|
68
63
|
# "former latter" or "big dog" where "big" is the former and "dog" is the latter.
|
69
64
|
def preceded_by(former)
|
70
65
|
matches = SearchAtom.new
|
71
|
-
latter =
|
66
|
+
latter = ActiveSupport::OrderedHash.new
|
72
67
|
former.record_ids.each do |rid|
|
73
68
|
latter[rid] = @records[rid] if @records[rid]
|
74
69
|
end
|
@@ -91,7 +86,7 @@ module ActsAsIndexed #:nodoc:
|
|
91
86
|
# Returns a hash of record_ids and weightings for each record in the
|
92
87
|
# atom.
|
93
88
|
def weightings(records_size)
|
94
|
-
out =
|
89
|
+
out = ActiveSupport::OrderedHash.new
|
95
90
|
@records.each do |r_id, pos|
|
96
91
|
|
97
92
|
# Fixes a bug when the records_size is zero. i.e. The only record
|
@@ -1,17 +1,12 @@
|
|
1
|
-
# ActsAsIndexed
|
2
|
-
# Copyright (c) 2007 - 2011 Douglas F Shearer.
|
3
|
-
# http://douglasfshearer.com
|
4
|
-
# Distributed under the MIT license as included with this plugin.
|
5
|
-
|
6
1
|
module ActsAsIndexed #:nodoc:
|
7
2
|
class SearchIndex
|
8
3
|
|
9
4
|
# fields:: Fields or instance methods of ActiveRecord model to be indexed.
|
10
5
|
# config:: ActsAsIndexed::Configuration instance.
|
11
6
|
def initialize(fields, config)
|
12
|
-
@storage = Storage.new(
|
7
|
+
@storage = Storage.new(config)
|
13
8
|
@fields = fields
|
14
|
-
@atoms =
|
9
|
+
@atoms = ActiveSupport::OrderedHash.new
|
15
10
|
@min_word_size = config.min_word_size
|
16
11
|
@records_size = @storage.record_count
|
17
12
|
@case_sensitive = config.case_sensitive
|
@@ -30,7 +25,7 @@ module ActsAsIndexed #:nodoc:
|
|
30
25
|
|
31
26
|
# Adds multiple records to the index. Accepts an array of +records+.
|
32
27
|
def add_records(records)
|
33
|
-
atoms =
|
28
|
+
atoms = ActiveSupport::OrderedHash.new
|
34
29
|
|
35
30
|
records.each do |record|
|
36
31
|
next unless @if_proc.call(record)
|
@@ -68,7 +63,7 @@ module ActsAsIndexed #:nodoc:
|
|
68
63
|
starts_with = run_queries(queries[:starts_with], true)
|
69
64
|
start_quoted = run_quoted_queries(queries[:start_quoted], true)
|
70
65
|
|
71
|
-
results =
|
66
|
+
results = ActiveSupport::OrderedHash.new
|
72
67
|
|
73
68
|
if queries[:start_quoted].any?
|
74
69
|
results = merge_query_results(results, start_quoted)
|
@@ -109,7 +104,7 @@ module ActsAsIndexed #:nodoc:
|
|
109
104
|
r1.merge(r2) { |r_id,old_val,new_val| old_val + new_val}
|
110
105
|
end
|
111
106
|
|
112
|
-
def add_occurences(condensed_record, record_id, atoms=
|
107
|
+
def add_occurences(condensed_record, record_id, atoms=ActiveSupport::OrderedHash.new)
|
113
108
|
condensed_record.each_with_index do |atom_name, i|
|
114
109
|
atoms[atom_name] = SearchAtom.new unless atoms.include?(atom_name)
|
115
110
|
atoms[atom_name].add_position(record_id, i)
|
@@ -144,9 +139,12 @@ module ActsAsIndexed #:nodoc:
|
|
144
139
|
end
|
145
140
|
|
146
141
|
# Find -foo.
|
142
|
+
# Ignores instances where a dash is used as a hyphen.
|
147
143
|
negative = []
|
148
|
-
|
149
|
-
negative << cleanup_atoms(
|
144
|
+
s.gsub!(/^(.*\s)?-(\S*)/) do |match|
|
145
|
+
negative << cleanup_atoms($2).first
|
146
|
+
|
147
|
+
$1
|
150
148
|
end
|
151
149
|
|
152
150
|
# Find +foo
|
@@ -168,9 +166,9 @@ module ActsAsIndexed #:nodoc:
|
|
168
166
|
end
|
169
167
|
|
170
168
|
def run_queries(atoms, starts_with=false)
|
171
|
-
results =
|
169
|
+
results = ActiveSupport::OrderedHash.new
|
172
170
|
atoms.each do |atom|
|
173
|
-
interim_results =
|
171
|
+
interim_results = ActiveSupport::OrderedHash.new
|
174
172
|
|
175
173
|
# If these atoms are to be run as 'starts with', make them a Regexp
|
176
174
|
# with a carat.
|
@@ -193,9 +191,9 @@ module ActsAsIndexed #:nodoc:
|
|
193
191
|
end
|
194
192
|
|
195
193
|
def run_quoted_queries(quoted_atoms, starts_with=false)
|
196
|
-
results =
|
194
|
+
results = ActiveSupport::OrderedHash.new
|
197
195
|
quoted_atoms.each do |quoted_atom|
|
198
|
-
interim_results =
|
196
|
+
interim_results = ActiveSupport::OrderedHash.new
|
199
197
|
|
200
198
|
break if quoted_atom.empty?
|
201
199
|
|
@@ -251,21 +249,26 @@ module ActsAsIndexed #:nodoc:
|
|
251
249
|
end
|
252
250
|
|
253
251
|
|
254
|
-
def cleanup_atoms(s, limit_size=false
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
atoms.reject{|w| w.size < min_size}
|
252
|
+
def cleanup_atoms(s, limit_size=false)
|
253
|
+
pre_tokenized = PreTokenizer.process(s)
|
254
|
+
tokenized = Tokenizer.process(pre_tokenized)
|
255
|
+
TokenNormalizer.process(tokenized, :normalize_case => !@case_sensitive, :min_token_length => !limit_size ? @min_token_length : false)
|
259
256
|
end
|
260
257
|
|
261
258
|
def condense_record(record)
|
262
|
-
|
259
|
+
atoms = []
|
260
|
+
|
263
261
|
@fields.each do |f|
|
264
262
|
if (value = record.send(f)).present?
|
265
|
-
|
263
|
+
atoms += cleanup_atoms(value.to_s)
|
264
|
+
|
265
|
+
#U+3000 separates fields so that quoted terms cannot match across
|
266
|
+
#fields
|
267
|
+
atoms << "\u3000"
|
266
268
|
end
|
267
269
|
end
|
268
|
-
|
270
|
+
|
271
|
+
atoms
|
269
272
|
end
|
270
273
|
|
271
274
|
end
|
@@ -1,8 +1,3 @@
|
|
1
|
-
# ActsAsIndexed
|
2
|
-
# Copyright (c) 2007 - 2011 Douglas F Shearer.
|
3
|
-
# http://douglasfshearer.com
|
4
|
-
# Distributed under the MIT license as included with this plugin.
|
5
|
-
|
6
1
|
module ActsAsIndexed
|
7
2
|
|
8
3
|
# Adds model class singleton methods.
|
@@ -17,4 +12,4 @@ module ActsAsIndexed
|
|
17
12
|
|
18
13
|
end
|
19
14
|
|
20
|
-
end
|
15
|
+
end
|
@@ -1,8 +1,3 @@
|
|
1
|
-
# ActsAsIndexed
|
2
|
-
# Copyright (c) 2007 - 2011 Douglas F Shearer.
|
3
|
-
# http://douglasfshearer.com
|
4
|
-
# Distributed under the MIT license as included with this plugin.
|
5
|
-
|
6
1
|
module ActsAsIndexed #:nodoc:
|
7
2
|
class Storage
|
8
3
|
|
@@ -11,10 +6,11 @@ module ActsAsIndexed #:nodoc:
|
|
11
6
|
INDEX_FILE_EXTENSION = '.ind'
|
12
7
|
TEMP_FILE_EXTENSION = '.tmp'
|
13
8
|
|
14
|
-
def initialize(
|
15
|
-
@path =
|
16
|
-
@size_path = path.join('size')
|
17
|
-
@prefix_size =
|
9
|
+
def initialize(config)
|
10
|
+
@path = Pathname.new(config.index_file.to_s)
|
11
|
+
@size_path = @path.join('size')
|
12
|
+
@prefix_size = config.index_file_depth
|
13
|
+
@is_windows_filesystem = config.is_windows_filesystem?
|
18
14
|
prepare
|
19
15
|
end
|
20
16
|
|
@@ -35,7 +31,7 @@ module ActsAsIndexed #:nodoc:
|
|
35
31
|
# Takes a string array of atoms names
|
36
32
|
# return a hash of the relevant atoms.
|
37
33
|
def fetch(atom_names, start=false)
|
38
|
-
atoms =
|
34
|
+
atoms = ActiveSupport::OrderedHash.new
|
39
35
|
|
40
36
|
atom_names.uniq.collect{|a| encoded_prefix(a) }.uniq.each do |prefix|
|
41
37
|
pattern = @path.join(prefix.to_s).to_s
|
@@ -74,7 +70,7 @@ module ActsAsIndexed #:nodoc:
|
|
74
70
|
# Sort the atoms into the appropriate shards for writing to individual
|
75
71
|
# files.
|
76
72
|
atoms.each do |atom_name, records|
|
77
|
-
(atoms_sorted[encoded_prefix(atom_name)] ||=
|
73
|
+
(atoms_sorted[encoded_prefix(atom_name)] ||= ActiveSupport::OrderedHash.new)[atom_name] = records
|
78
74
|
end
|
79
75
|
|
80
76
|
atoms_sorted.each do |e_p, atoms|
|
@@ -87,7 +83,7 @@ module ActsAsIndexed #:nodoc:
|
|
87
83
|
Marshal.load(f)
|
88
84
|
end
|
89
85
|
else
|
90
|
-
from_file =
|
86
|
+
from_file = ActiveSupport::OrderedHash.new
|
91
87
|
end
|
92
88
|
|
93
89
|
atoms = from_file.merge(atoms){ |k,o,n| o.send(operation, n) }
|
@@ -134,7 +130,7 @@ module ActsAsIndexed #:nodoc:
|
|
134
130
|
def encoded_prefix(atom)
|
135
131
|
prefix = atom[0, @prefix_size]
|
136
132
|
|
137
|
-
unless (@prefix_cache ||=
|
133
|
+
unless (@prefix_cache ||= ActiveSupport::OrderedHash.new).has_key?(prefix)
|
138
134
|
if atom.length > 1
|
139
135
|
@prefix_cache[prefix] = prefix.split(//).map{|c| encode_character(c)}.join('_')
|
140
136
|
else
|
@@ -155,42 +151,48 @@ module ActsAsIndexed #:nodoc:
|
|
155
151
|
end
|
156
152
|
|
157
153
|
def write_file(file_path)
|
158
|
-
|
159
|
-
|
154
|
+
new_file_name = file_path.to_s
|
155
|
+
temp_file_name = new_file_name + TEMP_FILE_EXTENSION
|
160
156
|
|
161
157
|
# Windows doesn't seem to play nice with writing then moving the file.
|
162
158
|
# https://github.com/dougal/acts_as_indexed/issues/15
|
163
|
-
writeable_file = windows? ?
|
159
|
+
writeable_file = windows? ? new_file_name : temp_file_name
|
164
160
|
|
165
161
|
File.open(writeable_file, 'w+') do |f|
|
166
162
|
yield(f)
|
167
163
|
end
|
168
164
|
|
169
|
-
FileUtils.mv(
|
165
|
+
FileUtils.mv(temp_file_name, new_file_name) unless windows?
|
170
166
|
end
|
171
167
|
|
168
|
+
@@file_lock = Mutex.new
|
169
|
+
|
172
170
|
# Borrowed from Rails' ActiveSupport FileStore. Also under MIT licence.
|
173
|
-
# Lock a file for a block so only one process can modify it at a time.
|
171
|
+
# Lock a file for a block so only one process or thread can modify it at a time.
|
174
172
|
def lock_file(file_path, &block) # :nodoc:
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
173
|
+
@@file_lock.synchronize do
|
174
|
+
|
175
|
+
# Windows does not support file locking.
|
176
|
+
if !windows? && file_path.exist?
|
177
|
+
file_path.open('r+') do |f|
|
178
|
+
begin
|
179
|
+
f.flock File::LOCK_EX
|
180
|
+
yield
|
181
|
+
ensure
|
182
|
+
f.flock File::LOCK_UN
|
183
|
+
end
|
183
184
|
end
|
185
|
+
else
|
186
|
+
yield
|
184
187
|
end
|
185
|
-
|
186
|
-
yield
|
188
|
+
|
187
189
|
end
|
188
190
|
end
|
189
191
|
|
190
192
|
# Checking for windows all the time seems costly.
|
191
193
|
# Write a separate windows storage class, and use it at runtime?
|
192
194
|
def windows?
|
193
|
-
|
195
|
+
@is_windows_filesystem
|
194
196
|
end
|
195
197
|
|
196
198
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module ActsAsIndexed
|
2
|
+
class TokenNormalizer
|
3
|
+
|
4
|
+
# Takes an array of tokens.
|
5
|
+
# - Downcases the tokens when :normalize_case option is passed.
|
6
|
+
# - Removes tokens of :min_token_length when option is passed.
|
7
|
+
# Returns the resulting array of tokens.
|
8
|
+
def self.process(arr, options={})
|
9
|
+
if options[:normalize_case]
|
10
|
+
arr = arr.map{ |t| t.downcase }
|
11
|
+
end
|
12
|
+
|
13
|
+
if options[:min_token_length]
|
14
|
+
arr = arr.reject{ |w| w.size < options[:min_token_length] }
|
15
|
+
end
|
16
|
+
|
17
|
+
arr
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
data/lib/will_paginate_search.rb
CHANGED
data/test/abstract_unit.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'test/unit'
|
2
2
|
require 'fileutils'
|
3
3
|
require 'rubygems'
|
4
|
+
|
5
|
+
require 'bundler/setup'
|
4
6
|
require 'active_record'
|
5
7
|
require 'active_record/fixtures'
|
6
8
|
require 'mocha'
|
@@ -16,6 +18,10 @@ class Rails
|
|
16
18
|
end
|
17
19
|
end
|
18
20
|
|
21
|
+
# Load will_paginate.
|
22
|
+
require 'will_paginate'
|
23
|
+
require 'will_paginate/collection'
|
24
|
+
|
19
25
|
test_path = Pathname.new(File.expand_path('../', __FILE__))
|
20
26
|
require test_path.parent.join('lib', 'acts_as_indexed').to_s
|
21
27
|
|
@@ -36,7 +42,7 @@ class ActiveSupport::TestCase #:nodoc:
|
|
36
42
|
self.use_instantiated_fixtures = false
|
37
43
|
|
38
44
|
def destroy_index
|
39
|
-
|
45
|
+
FileUtils.rm_rf(index_loc)
|
40
46
|
end
|
41
47
|
|
42
48
|
def build_index
|
@@ -66,7 +66,8 @@ class ActsAsIndexedTest < ActiveSupport::TestCase
|
|
66
66
|
queries = {
|
67
67
|
'crane -foo' => [5],
|
68
68
|
'-foo crane' => [5],
|
69
|
-
'-foo' => [] # edgecase
|
69
|
+
'-foo' => [], # negative only edgecase.
|
70
|
+
're-entered' => [5] # actually a hyphen edgecase.
|
70
71
|
}
|
71
72
|
|
72
73
|
run_queries(queries)
|
@@ -112,8 +113,8 @@ class ActsAsIndexedTest < ActiveSupport::TestCase
|
|
112
113
|
'^crane' => [6,5] ,
|
113
114
|
'^cran' => [6,5],
|
114
115
|
'^cra' => [6,5],
|
115
|
-
'^cr' => [6,5
|
116
|
-
'^c' => [5,2,1,6,
|
116
|
+
'^cr' => [6,4,5],
|
117
|
+
'^c' => [5,2,1,3,6,4],
|
117
118
|
'^notthere' => []
|
118
119
|
}
|
119
120
|
|
@@ -132,25 +133,29 @@ class ActsAsIndexedTest < ActiveSupport::TestCase
|
|
132
133
|
'^"crane"' => [6,5],
|
133
134
|
'^"cran"' => [6,5],
|
134
135
|
'^"cra"' => [6,5],
|
135
|
-
'^"cr"' => [6,5
|
136
|
-
'^"c"' => [5,2,1,6,
|
136
|
+
'^"cr"' => [6,4,5],
|
137
|
+
'^"c"' => [5,2,1,3,6,4],
|
137
138
|
}
|
138
139
|
|
139
140
|
run_queries(queries)
|
140
141
|
end
|
141
142
|
|
143
|
+
|
144
|
+
# NOTE: This test always fails for Rails 2.3. A bug somewhere in either
|
145
|
+
# Rails or the SQLite adaptor which causes the offset to be ignored.
|
146
|
+
# The offending assertions are not run in CI as a result.
|
142
147
|
def test_find_options
|
143
148
|
# limit.
|
144
149
|
assert_equal [6], Post.find_with_index('^cr', { :limit => 1 }, :ids_only => true)
|
145
150
|
assert_equal [6], Post.find_with_index('^cr', { :limit => 1 }).map{ |r| r.id }
|
146
151
|
|
147
152
|
# offset
|
148
|
-
assert_equal [5
|
149
|
-
assert_equal [5
|
153
|
+
assert_equal [4,5], Post.find_with_index('^cr', { :offset => 1 }, :ids_only => true)
|
154
|
+
assert_equal [4,5], Post.find_with_index('^cr', { :offset => 1 }).map{ |r| r.id }
|
150
155
|
|
151
156
|
# limit and offset
|
152
|
-
assert_equal [
|
153
|
-
assert_equal [
|
157
|
+
assert_equal [4], Post.find_with_index('^cr', { :limit => 1, :offset => 1 }, :ids_only => true)
|
158
|
+
assert_equal [4], Post.find_with_index('^cr', { :limit => 1, :offset => 1 }).map{ |r| r.id }
|
154
159
|
|
155
160
|
# order
|
156
161
|
assert_equal [6,5,4,3,2,1], Post.find_with_index('^c', { :order => 'id desc' }).map{ |r| r.id }
|
@@ -160,18 +165,20 @@ class ActsAsIndexedTest < ActiveSupport::TestCase
|
|
160
165
|
assert_equal [6,5,4], Post.find_with_index('^c', { :order => 'id desc' , :limit => 3}).map{ |r| r.id }
|
161
166
|
assert_equal [1,2,3,4], Post.find_with_index('^c', { :order => 'id', :limit => 4 }).map{ |r| r.id }
|
162
167
|
|
163
|
-
# order and offset
|
164
|
-
assert_equal [5,4,3,2,1], Post.find_with_index('^c', { :order => 'id desc' , :offset => 1}).map{ |r| r.id }
|
165
|
-
assert_equal [3,4,5,6], Post.find_with_index('^c', { :order => 'id', :offset => 2 }).map{ |r| r.id }
|
166
|
-
|
167
168
|
# order, limit and offset
|
168
169
|
assert_equal [5,4,3], Post.find_with_index('^c', { :order => 'id desc' , :limit => 3, :offset => 1}).map{ |r| r.id }
|
169
170
|
assert_equal [3,4], Post.find_with_index('^c', { :order => 'id', :limit => 2, :offset => 2 }).map{ |r| r.id }
|
171
|
+
|
172
|
+
# order and offset
|
173
|
+
unless ENV['CI'] && !Post.respond_to?(:where) # Rails < 3 does not respond to arel methods.
|
174
|
+
assert_equal [5,4,3,2,1], Post.find_with_index('^c', { :order => 'id desc' , :offset => 1}).map{ |r| r.id }
|
175
|
+
assert_equal [3,4,5,6], Post.find_with_index('^c', { :order => 'id', :offset => 2 }).map{ |r| r.id }
|
176
|
+
end
|
170
177
|
end
|
171
178
|
|
172
179
|
def test_should_error_when_ids_only_combined_with_finder_options
|
173
180
|
expected_message = "ids_only can not be combined with find option keys other than :offset or :limit"
|
174
|
-
|
181
|
+
|
175
182
|
error = assert_raise(ArgumentError) do
|
176
183
|
Post.find_with_index('foo', { :order => 'id' }, :ids_only => true)
|
177
184
|
end
|
@@ -266,6 +273,11 @@ class ActsAsIndexedTest < ActiveSupport::TestCase
|
|
266
273
|
assert_equal 5, Post.find_with_index('the', {}, { :no_query_cache => true, :ids_only => true}).size
|
267
274
|
end
|
268
275
|
|
276
|
+
def test_queries_across_field_boundaries
|
277
|
+
assert_equal [], Post.find_with_index('"Ellis Julien"', { :limit => 1 }, :ids_only => true)
|
278
|
+
assert_equal [], Post.find_with_index('"myself crane"', { :limit => 1 }, :ids_only => true)
|
279
|
+
end
|
280
|
+
|
269
281
|
private
|
270
282
|
|
271
283
|
def run_queries(queries)
|
data/test/configuration_test.rb
CHANGED
@@ -15,6 +15,15 @@ class ConfigurationTest < ActiveSupport::TestCase
|
|
15
15
|
assert_equal 3, config.min_word_size
|
16
16
|
end
|
17
17
|
|
18
|
+
def test_default_is_windows_filesystem_should_be_set
|
19
|
+
assert_equal !!RUBY_PLATFORM[/mswin32|mingw|cygwin/], config.is_windows_filesystem?
|
20
|
+
end
|
21
|
+
|
22
|
+
def tests_is_windows_filesystem_should_be_writeable
|
23
|
+
config.is_windows_filesystem = 'banana'
|
24
|
+
assert config.is_windows_filesystem?
|
25
|
+
end
|
26
|
+
|
18
27
|
def test_index_file_should_be_writeable
|
19
28
|
config.index_file = [Rails.root, 'my_index']
|
20
29
|
assert_equal Rails.root.join('my_index'), config.index_file
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'abstract_unit'
|
2
|
+
include ActsAsIndexed
|
3
|
+
|
4
|
+
class TokenNormalizerTest < ActiveSupport::TestCase
|
5
|
+
|
6
|
+
def test_leaves_case_of_tokens_untouched
|
7
|
+
assert_equal ["Chocolate", "Chip", "Cookies"], TokenNormalizer.process(["Chocolate", "Chip", "Cookies"])
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_downcases_tokens
|
11
|
+
assert_equal ["chocolate", "chip", "cookies"], TokenNormalizer.process(["Chocolate", "Chip", "Cookies"], :normalize_case => true)
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_limits_min_length_to_five
|
15
|
+
assert_equal ["Chocolate", "Cookies"], TokenNormalizer.process(["Chocolate", "Chip", "Cookies"], :min_token_length => 5)
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_limits_min_length_to_four
|
19
|
+
assert_equal ["Love", "Chocolate", "Chip", "Cookies"], TokenNormalizer.process(["I", "Love", "Chocolate", "Chip", "Cookies"], :min_token_length => 4)
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_downcases_and_limits_min_length_to_four
|
23
|
+
assert_equal ["love", "chocolate", "chip", "cookies"], TokenNormalizer.process(["I", "Love", "Chocolate", "Chip", "Cookies"], :normalize_case => true, :min_token_length => 4)
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'abstract_unit'
|
2
|
+
include ActsAsIndexed
|
3
|
+
|
4
|
+
class TokenizerTest < ActiveSupport::TestCase
|
5
|
+
|
6
|
+
def test_splits_tokens_to_array
|
7
|
+
assert_equal ["Chocolate", "Chip", "Cookies"], Tokenizer.process("Chocolate Chip Cookies ")
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_deals_with_multiple_spaces
|
11
|
+
assert_equal ["Chocolate", "Chip", "Cookies"], Tokenizer.process("Chocolate Chip Cookies ")
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'abstract_unit'
|
2
|
+
|
3
|
+
class WillPaginateSearchTest < ActiveSupport::TestCase
|
4
|
+
fixtures :posts
|
5
|
+
|
6
|
+
def teardown
|
7
|
+
# need to do this to work with the :if Proc tests.
|
8
|
+
Post.acts_as_indexed :fields => [:title, :body]
|
9
|
+
destroy_index
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_paginate_search
|
13
|
+
|
14
|
+
assert_equal [5,2,1,3,6,4], paginate_search(1, 10)
|
15
|
+
assert_equal [5,2,1], paginate_search(1, 3)
|
16
|
+
assert_equal [3,6,4], paginate_search(2, 3)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
# Returns relevant IDs.
|
22
|
+
def paginate_search(page, per_page)
|
23
|
+
Post.paginate_search('^c', :page => page, :per_page => per_page).map { |p| p.id }
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: acts_as_indexed
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
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: 2012-
|
12
|
+
date: 2012-12-20 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: Acts As Indexed is a plugin which provides a pain-free way to add fulltext
|
15
15
|
search to your Ruby on Rails app
|
@@ -19,6 +19,7 @@ extensions: []
|
|
19
19
|
extra_rdoc_files:
|
20
20
|
- README.rdoc
|
21
21
|
files:
|
22
|
+
- .travis.yml
|
22
23
|
- CHANGELOG
|
23
24
|
- Gemfile
|
24
25
|
- Gemfile.lock
|
@@ -27,14 +28,21 @@ files:
|
|
27
28
|
- Rakefile
|
28
29
|
- VERSION
|
29
30
|
- acts_as_indexed.gemspec
|
31
|
+
- gemfiles/rails2_3.gemfile
|
32
|
+
- gemfiles/rails3_0.gemfile
|
33
|
+
- gemfiles/rails3_1.gemfile
|
34
|
+
- gemfiles/rails3_2.gemfile
|
30
35
|
- lib/acts_as_indexed.rb
|
31
36
|
- lib/acts_as_indexed/class_methods.rb
|
32
37
|
- lib/acts_as_indexed/configuration.rb
|
33
38
|
- lib/acts_as_indexed/instance_methods.rb
|
39
|
+
- lib/acts_as_indexed/pre_tokenizer.rb
|
34
40
|
- lib/acts_as_indexed/search_atom.rb
|
35
41
|
- lib/acts_as_indexed/search_index.rb
|
36
42
|
- lib/acts_as_indexed/singleton_methods.rb
|
37
43
|
- lib/acts_as_indexed/storage.rb
|
44
|
+
- lib/acts_as_indexed/token_normalizer.rb
|
45
|
+
- lib/acts_as_indexed/tokenizer.rb
|
38
46
|
- lib/will_paginate_search.rb
|
39
47
|
- rails/init.rb
|
40
48
|
- test/abstract_unit.rb
|
@@ -43,9 +51,13 @@ files:
|
|
43
51
|
- test/database.yml
|
44
52
|
- test/fixtures/post.rb
|
45
53
|
- test/fixtures/posts.yml
|
54
|
+
- test/pre_tokenizer_test.rb
|
46
55
|
- test/schema.rb
|
47
56
|
- test/search_atom_test.rb
|
48
57
|
- test/search_index_test.rb
|
58
|
+
- test/token_normalizer_test.rb
|
59
|
+
- test/tokenizer_test.rb
|
60
|
+
- test/will_paginate_search_test.rb
|
49
61
|
homepage: http://github.com/dougal/acts_as_indexed
|
50
62
|
licenses: []
|
51
63
|
post_install_message:
|
@@ -66,7 +78,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
66
78
|
version: '0'
|
67
79
|
requirements: []
|
68
80
|
rubyforge_project:
|
69
|
-
rubygems_version: 1.8.
|
81
|
+
rubygems_version: 1.8.23
|
70
82
|
signing_key:
|
71
83
|
specification_version: 3
|
72
84
|
summary: Acts As Indexed is a plugin which provides a pain-free way to add fulltext
|