acts_as_indexed 0.6.2 → 0.6.3
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +6 -0
- data/README.rdoc +33 -20
- data/VERSION +1 -1
- data/acts_as_indexed.gemspec +2 -2
- data/lib/acts_as_indexed/configuration.rb +29 -9
- data/lib/acts_as_indexed/search_atom.rb +2 -2
- data/lib/acts_as_indexed/search_index.rb +41 -42
- data/lib/acts_as_indexed.rb +32 -30
- data/lib/will_paginate_search.rb +9 -5
- data/test/abstract_unit.rb +14 -13
- data/test/acts_as_indexed_test.rb +69 -5
- data/test/configuration_test.rb +15 -15
- data/test/fixtures/posts.yml +8 -2
- data/test/schema.rb +1 -0
- data/test/search_atom_test.rb +1 -1
- data/test/search_index_test.rb +1 -1
- metadata +4 -4
data/CHANGELOG
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
===0.6.3 [5th July 2010]
|
2
|
+
- index file path can now be definited as a Pathname as well as an array. [parndt]
|
3
|
+
- Can now define which records are indexed and which are not via an :if proc. [madpilot]
|
4
|
+
- Lots of tidying up. [parndt]
|
5
|
+
- Rails 3 fixes. [myabc]
|
6
|
+
|
1
7
|
===0.6.2 [11th June 2010]
|
2
8
|
- Now available as a Gem as well as the original plugin. [parndt - Thanks for doing most of the hard work.]
|
3
9
|
|
data/README.rdoc
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
= acts_as_indexed
|
2
2
|
|
3
|
-
If you find this plugin useful, please consider a donation to show your
|
3
|
+
If you find this plugin useful, please consider a donation to show your
|
4
4
|
support!
|
5
5
|
|
6
6
|
http://www.paypal.com/cgi-bin/webscr?cmd=_send-money
|
7
|
-
|
7
|
+
|
8
8
|
Paypal address: mailto:dougal.s@gmail.com
|
9
|
-
|
9
|
+
|
10
10
|
|
11
11
|
== Instructions
|
12
12
|
|
13
|
-
This plugin allows boolean-queried fulltext search to be added to any Rails
|
13
|
+
This plugin allows boolean-queried fulltext search to be added to any Rails
|
14
14
|
app with no dependencies and minimal setup.
|
15
15
|
|
16
16
|
|
@@ -25,19 +25,19 @@ app with no dependencies and minimal setup.
|
|
25
25
|
rails plugin install git://github.com/dougal/acts_as_indexed.git
|
26
26
|
|
27
27
|
=== As a Gem
|
28
|
-
Despite this being slightly against the the original ethos of the project,
|
28
|
+
Despite this being slightly against the the original ethos of the project,
|
29
29
|
acts_as_indexed is now available as a Gem as several people have requested it.
|
30
|
-
|
30
|
+
|
31
31
|
gem install acts_as_indexed
|
32
|
-
|
32
|
+
|
33
33
|
Make sure to specify the Gem in your environment.rb file (Rails 2.x.x), or the Gemfile (Rails 3.x.x).
|
34
34
|
|
35
|
-
If you don't have git installed, you can download the plugin from the GitHub
|
36
|
-
page (http://github.com/dougal/acts_as_indexed) and unpack it into the
|
37
|
-
<tt>vendor/plugins</tt> directory of your rails app.
|
35
|
+
If you don't have git installed, you can download the plugin from the GitHub
|
36
|
+
page (http://github.com/dougal/acts_as_indexed) and unpack it into the
|
37
|
+
<tt>vendor/plugins</tt> directory of your rails app.
|
38
38
|
|
39
|
-
== Usage
|
40
39
|
|
40
|
+
== Usage
|
41
41
|
|
42
42
|
=== Setup
|
43
43
|
|
@@ -46,7 +46,7 @@ list of the fields you wish to be indexed.
|
|
46
46
|
|
47
47
|
class Post < ActiveRecord::Base
|
48
48
|
acts_as_indexed :fields => [:title, :body]
|
49
|
-
|
49
|
+
|
50
50
|
...
|
51
51
|
end
|
52
52
|
|
@@ -62,22 +62,33 @@ the current model.
|
|
62
62
|
|
63
63
|
...
|
64
64
|
end
|
65
|
-
|
65
|
+
|
66
66
|
Any of the configuration options in the Further Configuration section can be added as to the acts_as_indexed method call. These will override any defaults or global configuration.
|
67
67
|
|
68
|
+
You can specify proc that needs to evaluate to true before the item gets indexed.
|
69
|
+
This is useful if you only want items with a certain state to be included.
|
70
|
+
The Proc is passed the current object's instance so you are able to test against that.
|
71
|
+
|
72
|
+
For example, if you have a visible column that is false if the post is hidden, or true
|
73
|
+
if it is visible, you can filter the index by doing:
|
74
|
+
|
75
|
+
class Post < ActiveRecord::Base
|
76
|
+
acts_as_indexed :fields => [:title, :body], :if => Proc.new { |post| post.visible? }
|
77
|
+
...
|
78
|
+
end
|
68
79
|
|
69
80
|
=== Searching
|
70
81
|
|
71
|
-
To search, call the +with_query+ named scope on your model, passing a query as
|
82
|
+
To search, call the +with_query+ named scope on your model, passing a query as
|
72
83
|
an argument.
|
73
84
|
|
74
85
|
# Returns array of Post objects.
|
75
86
|
my_search_results = Post.with_query('my search query')
|
76
|
-
|
87
|
+
|
77
88
|
# Chain it with any number of ActiveRecord methods and named_scopes.
|
78
89
|
my_search_results = Post.public.with_query('my search query').find(:all, :limit => 10) # return the first 10 matches which are public.
|
79
|
-
|
80
|
-
|
90
|
+
|
91
|
+
|
81
92
|
=== Query Options
|
82
93
|
|
83
94
|
The following query operators are supported:
|
@@ -89,7 +100,7 @@ The following query operators are supported:
|
|
89
100
|
|
90
101
|
=== Pagination
|
91
102
|
|
92
|
-
Since +with_query+ is a named scope, WillPaginate can be used in the normal
|
103
|
+
Since +with_query+ is a named scope, WillPaginate can be used in the normal
|
93
104
|
fashion.
|
94
105
|
|
95
106
|
@images = Image.with_query('girl').paginate(:page => 1, :per_page => 5)
|
@@ -108,15 +119,17 @@ Example showing defaults:
|
|
108
119
|
A full rundown of the available configuration options can be found in
|
109
120
|
<tt>lib/acts_as_indexed/configuration.rb</tt>
|
110
121
|
|
122
|
+
|
111
123
|
== RDoc Documentation
|
112
124
|
|
113
125
|
To generate the RDoc documentation, run the <tt>rake rdoc</tt> task in the
|
114
126
|
acts_as_indexed plugin folder. Then point your web browser at
|
115
127
|
<tt>vendor/plugins/acts_as_indexed/rdoc/index.html</tt>.
|
116
128
|
|
117
|
-
Alternatively, you can view the rdoc documentation
|
129
|
+
Alternatively, you can view the rdoc documentation
|
118
130
|
online[http://rdoc.info/projects/dougal/acts_as_indexed/].
|
119
131
|
|
132
|
+
|
120
133
|
== Problems, Comments, Suggestions?
|
121
134
|
|
122
135
|
All of the above are most welcome. mailto:dougal.s@gmail.com
|
@@ -133,5 +146,5 @@ Future releases will be looking to add the following features:
|
|
133
146
|
* Optional html scrubbing during indexing.
|
134
147
|
* Ranking affected by field weightings.
|
135
148
|
* Support for DataMapper, Sequel and the various MongoDB ORMs.
|
136
|
-
* UTF-8 support. See the current solution here:
|
149
|
+
* UTF-8 support. See the current solution here:
|
137
150
|
https://gist.github.com/193903bb4e0d6e5debe1
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.6.
|
1
|
+
0.6.3
|
data/acts_as_indexed.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{acts_as_indexed}
|
8
|
-
s.version = "0.6.
|
8
|
+
s.version = "0.6.3"
|
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 = %q{2010-
|
12
|
+
s.date = %q{2010-07-05}
|
13
13
|
s.description = %q{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 = %q{dougal.s@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -6,36 +6,56 @@
|
|
6
6
|
module ActsAsIndexed
|
7
7
|
# Used to set up and modify settings for acts_as_indexed.
|
8
8
|
class Configuration
|
9
|
-
|
9
|
+
|
10
10
|
# Sets the location for the index. Specify as an array. Heroku, for
|
11
11
|
# example would use RAILS_ROOT/tmp/index, which would be set as
|
12
12
|
# [Rails.root,'tmp','index]
|
13
13
|
attr_accessor :index_file
|
14
|
-
|
14
|
+
|
15
15
|
# Tuning value for the index partitioning. Larger values result in quicker
|
16
16
|
# searches, but slower indexing. Default is 3.
|
17
17
|
attr_reader :index_file_depth
|
18
|
-
|
18
|
+
|
19
19
|
# Sets the minimum length for a word in a query. Words shorter than this
|
20
20
|
# value are ignored in searches unless preceded by the '+' operator.
|
21
21
|
# Default is 3.
|
22
22
|
attr_reader :min_word_size
|
23
|
-
|
23
|
+
|
24
|
+
# Proc that allows you to turn on or off index for a record.
|
25
|
+
# Useful if you don't want the index to be updated if the target model is
|
26
|
+
# should not return up in results, such as a draft post.
|
27
|
+
attr_accessor :if_proc
|
28
|
+
|
24
29
|
def initialize
|
25
|
-
@index_file =
|
30
|
+
@index_file = Rails.root.join('index') if Rails.root
|
26
31
|
@index_file_depth = 3
|
27
32
|
@min_word_size = 3
|
33
|
+
@if_proc = if_proc
|
34
|
+
end
|
35
|
+
|
36
|
+
def index_file=(file_path)
|
37
|
+
# Under the old syntax this was an array of path parts.
|
38
|
+
# If this is still using the array then rewrite to a Pathname.
|
39
|
+
if file_path.is_a?(Pathname)
|
40
|
+
@index_file = file_path
|
41
|
+
else
|
42
|
+
@index_file = Pathname.new(file_path.collect{|part| part.to_s}.join(File::SEPARATOR))
|
43
|
+
end
|
28
44
|
end
|
29
|
-
|
45
|
+
|
30
46
|
def index_file_depth=(val)
|
31
47
|
raise(ArgumentError, 'index_file_depth cannot be less than one (1)') if val < 1
|
32
48
|
@index_file_depth = val
|
33
49
|
end
|
34
|
-
|
50
|
+
|
35
51
|
def min_word_size=(val)
|
36
52
|
raise(ArgumentError, 'min_word_size cannot be less than one (1)') if val < 1
|
37
53
|
@min_word_size = val
|
38
54
|
end
|
39
|
-
|
55
|
+
|
56
|
+
def if_proc
|
57
|
+
@if_proc ||= Proc.new{true}
|
58
|
+
end
|
59
|
+
|
40
60
|
end
|
41
|
-
end
|
61
|
+
end
|
@@ -25,7 +25,7 @@ module ActsAsIndexed #:nodoc:
|
|
25
25
|
|
26
26
|
# Adds +record_id+ to the stored records.
|
27
27
|
def add_record(record_id)
|
28
|
-
@records[record_id] = []
|
28
|
+
@records[record_id] = [] unless include_record?(record_id)
|
29
29
|
end
|
30
30
|
|
31
31
|
# Adds +pos+ to the array of positions for +record_id+.
|
@@ -64,7 +64,7 @@ module ActsAsIndexed #:nodoc:
|
|
64
64
|
pos.each do |p|
|
65
65
|
# Check if previous position is in former.
|
66
66
|
if former.include_position?(record_id,p-1)
|
67
|
-
matches.add_record(record_id)
|
67
|
+
matches.add_record(record_id) unless matches.include_record?(record_id)
|
68
68
|
matches.add_position(record_id,p)
|
69
69
|
end
|
70
70
|
end
|
@@ -6,21 +6,24 @@
|
|
6
6
|
module ActsAsIndexed #:nodoc:
|
7
7
|
class SearchIndex
|
8
8
|
|
9
|
-
# root:: Location of index on filesystem.
|
9
|
+
# root:: Location of index on filesystem as a Pathname.
|
10
10
|
# index_depth:: Degree of index partitioning.
|
11
11
|
# fields:: Fields or instance methods of ActiveRecord model to be indexed.
|
12
12
|
# min_word_size:: Smallest query term that will be run through search.
|
13
|
-
|
14
|
-
|
13
|
+
# if_proc:: A Proc. If the proc is true, the index gets added, if false if doesn't
|
14
|
+
def initialize(root, index_depth, fields, min_word_size, if_proc=Proc.new{true})
|
15
|
+
@root = Pathname.new(root.to_s)
|
15
16
|
@fields = fields
|
16
17
|
@index_depth = index_depth
|
17
18
|
@atoms = {}
|
18
19
|
@min_word_size = min_word_size
|
19
20
|
@records_size = exists? ? load_record_size : 0
|
21
|
+
@if_proc = if_proc
|
20
22
|
end
|
21
23
|
|
22
24
|
# Adds +record+ to the index.
|
23
25
|
def add_record(record)
|
26
|
+
return @records_size unless @if_proc.call(record)
|
24
27
|
condensed_record = condense_record(record)
|
25
28
|
load_atoms(condensed_record)
|
26
29
|
add_occurences(condensed_record,record.id)
|
@@ -57,11 +60,13 @@ module ActsAsIndexed #:nodoc:
|
|
57
60
|
@atoms[a].remove_record(record_new.id) if @atoms.has_key?(a)
|
58
61
|
end
|
59
62
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
63
|
+
if @if_proc.call(record_new)
|
64
|
+
# Add the new version to the appropriate atoms.
|
65
|
+
load_atoms(new_atoms)
|
66
|
+
# TODO: Make a version of this method that takes the
|
67
|
+
# atomised version of the record.
|
68
|
+
add_occurences(new_atoms, record_new.id)
|
69
|
+
end
|
65
70
|
end
|
66
71
|
|
67
72
|
# Saves the current index partitions to the filesystem.
|
@@ -69,13 +74,11 @@ module ActsAsIndexed #:nodoc:
|
|
69
74
|
prepare
|
70
75
|
atoms_sorted = {}
|
71
76
|
@atoms.each do |atom_name, records|
|
72
|
-
|
73
|
-
atoms_sorted[e_p] = {} if !atoms_sorted.has_key?(e_p)
|
74
|
-
atoms_sorted[e_p][atom_name] = records
|
77
|
+
(atoms_sorted[encoded_prefix(atom_name)] ||= {})[atom_name] = records
|
75
78
|
end
|
76
79
|
atoms_sorted.each do |e_p, atoms|
|
77
80
|
#p "Saving #{e_p}."
|
78
|
-
|
81
|
+
@root.join(e_p.to_s).open("w+") do |f|
|
79
82
|
Marshal.dump(atoms,f)
|
80
83
|
end
|
81
84
|
end
|
@@ -86,8 +89,7 @@ module ActsAsIndexed #:nodoc:
|
|
86
89
|
#--
|
87
90
|
# TODO: Write a public method that will delete all indexes.
|
88
91
|
def destroy
|
89
|
-
|
90
|
-
true
|
92
|
+
@root.delete
|
91
93
|
end
|
92
94
|
|
93
95
|
# Returns an array of IDs for records matching +query+.
|
@@ -100,11 +102,11 @@ module ActsAsIndexed #:nodoc:
|
|
100
102
|
negative = run_queries(queries[:negative])
|
101
103
|
negative_quoted = run_quoted_queries(queries[:negative_quoted])
|
102
104
|
|
103
|
-
if
|
104
|
-
p = positive.delete_if{ |r_id,w|
|
105
|
-
pq = positive_quoted.delete_if{ |r_id,w|
|
105
|
+
if queries[:positive_quoted].any? && queries[:positive].any?
|
106
|
+
p = positive.delete_if{ |r_id,w| positive_quoted.exclude?(r_id) }
|
107
|
+
pq = positive_quoted.delete_if{ |r_id,w| positive.exclude?(r_id) }
|
106
108
|
results = p.merge(pq) { |r_id,old_val,new_val| old_val + new_val}
|
107
|
-
elsif
|
109
|
+
elsif queries[:positive].any?
|
108
110
|
results = positive
|
109
111
|
else
|
110
112
|
results = positive_quoted
|
@@ -120,21 +122,22 @@ module ActsAsIndexed #:nodoc:
|
|
120
122
|
#--
|
121
123
|
# TODO: Make a private method called 'root_exists?' which checks for the root directory.
|
122
124
|
def exists?
|
123
|
-
|
125
|
+
@root.join('size').exist?
|
124
126
|
end
|
125
127
|
|
126
128
|
private
|
127
129
|
|
128
130
|
# Gets the size file from the index.
|
129
131
|
def load_record_size
|
130
|
-
|
131
|
-
|
132
|
+
#p "About to load #{@root.join('size')}"
|
133
|
+
@root.join('size').open do |f|
|
134
|
+
Marshal.load(f)
|
132
135
|
end
|
133
136
|
end
|
134
137
|
|
135
138
|
# Saves the size to the size file.
|
136
139
|
def save_record_size
|
137
|
-
|
140
|
+
@root.join('size').open('w+') do |f|
|
138
141
|
Marshal.dump(@records_size,f)
|
139
142
|
end
|
140
143
|
end
|
@@ -147,7 +150,7 @@ module ActsAsIndexed #:nodoc:
|
|
147
150
|
# Returns true if all the given atoms are present.
|
148
151
|
def include_atoms?(atoms_arr)
|
149
152
|
atoms_arr.each do |a|
|
150
|
-
return false
|
153
|
+
return false unless include_atom?(a)
|
151
154
|
end
|
152
155
|
true
|
153
156
|
end
|
@@ -160,7 +163,7 @@ module ActsAsIndexed #:nodoc:
|
|
160
163
|
end
|
161
164
|
|
162
165
|
def add_atom(atom)
|
163
|
-
@atoms[atom] = SearchAtom.new
|
166
|
+
@atoms[atom] = SearchAtom.new unless include_atom?(atom)
|
164
167
|
end
|
165
168
|
|
166
169
|
def add_occurences(condensed_record,record_id)
|
@@ -173,10 +176,8 @@ module ActsAsIndexed #:nodoc:
|
|
173
176
|
|
174
177
|
def encoded_prefix(atom)
|
175
178
|
prefix = atom[0,@index_depth]
|
176
|
-
|
177
|
-
|
178
|
-
len = atom.length
|
179
|
-
if len > 1
|
179
|
+
unless (@prefix_cache ||= {}).has_key?(prefix)
|
180
|
+
if atom.length > 1
|
180
181
|
@prefix_cache[prefix] = prefix.split(//).map{|c| encode_character(c)}.join('_')
|
181
182
|
else
|
182
183
|
@prefix_cache[prefix] = encode_character(atom)
|
@@ -261,7 +262,7 @@ module ActsAsIndexed #:nodoc:
|
|
261
262
|
# return atom containing records + positions where current atom is preceded by following atom.
|
262
263
|
# end
|
263
264
|
# return records from final atom.
|
264
|
-
next
|
265
|
+
next unless include_atoms?(quoted_atom)
|
265
266
|
matches = @atoms[quoted_atom.first]
|
266
267
|
quoted_atom[1..-1].each do |atom_name|
|
267
268
|
matches = @atoms[atom_name].preceded_by(matches)
|
@@ -290,8 +291,8 @@ module ActsAsIndexed #:nodoc:
|
|
290
291
|
# Calculate prefixes.
|
291
292
|
# Remove duplicate prefixes.
|
292
293
|
atoms.uniq.reject{|a| include_atom?(a)}.collect{|a| encoded_prefix(a)}.uniq.each do |name|
|
293
|
-
if
|
294
|
-
|
294
|
+
if (atom_file = @root.join(name.to_s)).exist?
|
295
|
+
atom_file.open do |f|
|
295
296
|
@atoms.merge!(Marshal.load(f))
|
296
297
|
end
|
297
298
|
end
|
@@ -299,27 +300,25 @@ module ActsAsIndexed #:nodoc:
|
|
299
300
|
end
|
300
301
|
|
301
302
|
def prepare
|
302
|
-
# Makes the RAILS_ROOT/index
|
303
|
-
|
304
|
-
# Makes the RAILS_ROOT/index/ENVIRONMENT directory
|
305
|
-
Dir.mkdir(File.join(@root[0,3])) if !File.exists?(File.join(@root[0,3]))
|
306
|
-
# Makes the RAILS_ROOT/index/ENVIRONMENT/CLASS directory
|
307
|
-
Dir.mkdir(File.join(@root)) if !File.exists?(File.join(@root))
|
303
|
+
# Makes the RAILS_ROOT/index/ENVIRONMENT/CLASS directories
|
304
|
+
@root.mkpath
|
308
305
|
end
|
309
306
|
|
310
307
|
def cleanup_atoms(s, limit_size=false, min_size = @min_word_size || 3)
|
311
308
|
atoms = s.downcase.gsub(/\W/,' ').squeeze(' ').split
|
312
|
-
return atoms
|
309
|
+
return atoms unless limit_size
|
313
310
|
atoms.reject{|w| w.size < min_size}
|
314
311
|
end
|
315
312
|
|
316
313
|
def condense_record(record)
|
317
|
-
|
314
|
+
condensed = []
|
318
315
|
@fields.each do |f|
|
319
|
-
|
316
|
+
if (value = record.send(f)).present?
|
317
|
+
condensed << value.to_s
|
318
|
+
end
|
320
319
|
end
|
321
|
-
cleanup_atoms(
|
320
|
+
cleanup_atoms(condensed.join(' '))
|
322
321
|
end
|
323
322
|
|
324
323
|
end
|
325
|
-
end
|
324
|
+
end
|
data/lib/acts_as_indexed.rb
CHANGED
@@ -10,13 +10,13 @@ require 'acts_as_indexed/search_index'
|
|
10
10
|
require 'acts_as_indexed/search_atom'
|
11
11
|
|
12
12
|
module ActsAsIndexed #:nodoc:
|
13
|
-
|
13
|
+
|
14
14
|
# Holds the default configuration for acts_as_indexed.
|
15
|
-
|
15
|
+
|
16
16
|
@configuration = Configuration.new
|
17
17
|
|
18
18
|
# Returns the current configuration for acts_as_indexed.
|
19
|
-
|
19
|
+
|
20
20
|
def self.configuration
|
21
21
|
@configuration
|
22
22
|
end
|
@@ -29,7 +29,7 @@ module ActsAsIndexed #:nodoc:
|
|
29
29
|
# config.index_file_depth = 3
|
30
30
|
# config.min_word_size = 3
|
31
31
|
# end
|
32
|
-
|
32
|
+
|
33
33
|
def self.configure
|
34
34
|
self.configuration ||= Configuration.new
|
35
35
|
yield(configuration)
|
@@ -52,7 +52,7 @@ module ActsAsIndexed #:nodoc:
|
|
52
52
|
# min_word_size:: Sets the minimum length for a word in a query. Words
|
53
53
|
# shorter than this value are ignored in searches
|
54
54
|
# unless preceded by the '+' operator. Default is 3.
|
55
|
-
# index_file:: Sets the location for the index. By default this is
|
55
|
+
# index_file:: Sets the location for the index. By default this is
|
56
56
|
# RAILS_ROOT/index. Specify as an array. Heroku, for
|
57
57
|
# example would use RAILS_ROOT/tmp/index, which would be
|
58
58
|
# set as [Rails.root,'tmp','index]
|
@@ -64,7 +64,7 @@ module ActsAsIndexed #:nodoc:
|
|
64
64
|
include ActsAsIndexed::InstanceMethods
|
65
65
|
|
66
66
|
after_create :add_to_index
|
67
|
-
before_update
|
67
|
+
before_update :update_index
|
68
68
|
after_destroy :remove_from_index
|
69
69
|
|
70
70
|
# scope for Rails 3.x, named_scope for Rails 2.x.
|
@@ -73,24 +73,27 @@ module ActsAsIndexed #:nodoc:
|
|
73
73
|
else
|
74
74
|
named_scope :with_query, lambda { |query| { :conditions => ["#{table_name}.id IN (?)", search_index(query, {}, {:ids_only => true}) ] } }
|
75
75
|
end
|
76
|
-
|
76
|
+
|
77
77
|
cattr_accessor :aai_config, :aai_fields
|
78
78
|
|
79
79
|
self.aai_fields = options.delete(:fields)
|
80
80
|
raise(ArgumentError, 'no fields specified') if self.aai_fields.nil? || self.aai_fields.empty?
|
81
|
-
|
81
|
+
|
82
82
|
self.aai_config = ActsAsIndexed.configuration.dup
|
83
|
+
self.aai_config.if_proc = options.delete(:if)
|
83
84
|
options.each do |k, v|
|
84
85
|
self.aai_config.send("#{k}=", v)
|
85
86
|
end
|
86
|
-
|
87
|
+
|
88
|
+
# Add the Rails environment and this model's name to the index file path.
|
89
|
+
self.aai_config.index_file = self.aai_config.index_file.join(Rails.env, self.name)
|
87
90
|
end
|
88
91
|
|
89
92
|
# Adds the passed +record+ to the index. Index is built if it does not already exist. Clears the query cache.
|
90
93
|
|
91
94
|
def index_add(record)
|
92
|
-
build_index
|
93
|
-
index = SearchIndex.new(aai_config.index_file, aai_config.index_file_depth, aai_fields, aai_config.min_word_size)
|
95
|
+
build_index unless aai_config.index_file.directory?
|
96
|
+
index = SearchIndex.new(aai_config.index_file, aai_config.index_file_depth, aai_fields, aai_config.min_word_size, aai_config.if_proc)
|
94
97
|
index.add_record(record)
|
95
98
|
index.save
|
96
99
|
@query_cache = {}
|
@@ -100,22 +103,22 @@ module ActsAsIndexed #:nodoc:
|
|
100
103
|
# Removes the passed +record+ from the index. Clears the query cache.
|
101
104
|
|
102
105
|
def index_remove(record)
|
103
|
-
index = SearchIndex.new(aai_config.index_file, aai_config.index_file_depth, aai_fields, aai_config.min_word_size)
|
106
|
+
index = SearchIndex.new(aai_config.index_file, aai_config.index_file_depth, aai_fields, aai_config.min_word_size, aai_config.if_proc)
|
104
107
|
# record won't be in index if it doesn't exist. Just return true.
|
105
|
-
return true
|
108
|
+
return true unless index.exists?
|
106
109
|
index.remove_record(record)
|
107
110
|
index.save
|
108
111
|
@query_cache = {}
|
109
112
|
true
|
110
113
|
end
|
111
|
-
|
114
|
+
|
112
115
|
# Updates the index.
|
113
116
|
# 1. Removes the previous version of the record from the index
|
114
117
|
# 2. Adds the new version to the index.
|
115
|
-
|
118
|
+
|
116
119
|
def index_update(record)
|
117
|
-
build_index
|
118
|
-
index = SearchIndex.new(aai_config.index_file, aai_config.index_file_depth, aai_fields, aai_config.min_word_size)
|
120
|
+
build_index unless aai_config.index_file.directory?
|
121
|
+
index = SearchIndex.new(aai_config.index_file, aai_config.index_file_depth, aai_fields, aai_config.min_word_size, aai_config.if_proc)
|
119
122
|
#index.remove_record(find(record.id))
|
120
123
|
#index.add_record(record)
|
121
124
|
index.update_record(record,find(record.id))
|
@@ -133,7 +136,7 @@ module ActsAsIndexed #:nodoc:
|
|
133
136
|
#
|
134
137
|
# ====find_options
|
135
138
|
# Same as ActiveRecord#find options hash. An :order key will override
|
136
|
-
# the relevance ranking
|
139
|
+
# the relevance ranking
|
137
140
|
#
|
138
141
|
# ====options
|
139
142
|
# ids_only:: Method returns an array of integer IDs when set to true.
|
@@ -144,30 +147,29 @@ module ActsAsIndexed #:nodoc:
|
|
144
147
|
@query_cache = {} if (options.has_key?('no_query_cache') || options[:no_query_cache])
|
145
148
|
if !@query_cache || !@query_cache[query]
|
146
149
|
logger.debug('Query not in cache, running search.')
|
147
|
-
build_index
|
148
|
-
index = SearchIndex.new(aai_config.index_file, aai_config.index_file_depth, aai_fields, aai_config.min_word_size)
|
149
|
-
@query_cache
|
150
|
-
@query_cache[query] = index.search(query)
|
150
|
+
build_index unless aai_config.index_file.directory?
|
151
|
+
index = SearchIndex.new(aai_config.index_file, aai_config.index_file_depth, aai_fields, aai_config.min_word_size, aai_config.if_proc)
|
152
|
+
(@query_cache ||= {})[query] = index.search(query)
|
151
153
|
else
|
152
154
|
logger.debug('Query held in cache.')
|
153
155
|
end
|
154
156
|
return @query_cache[query].sort.reverse.map(&:first) if options[:ids_only] || @query_cache[query].empty?
|
155
|
-
|
157
|
+
|
156
158
|
# slice up the results by offset and limit
|
157
159
|
offset = find_options[:offset] || 0
|
158
160
|
limit = find_options.include?(:limit) ? find_options[:limit] : @query_cache[query].size
|
159
161
|
part_query = @query_cache[query].sort.reverse.slice(offset,limit).map(&:first)
|
160
|
-
|
162
|
+
|
161
163
|
# Set these to nil as we are dealing with the pagination by setting
|
162
164
|
# exactly what records we want.
|
163
165
|
find_options[:offset] = nil
|
164
166
|
find_options[:limit] = nil
|
165
|
-
|
167
|
+
|
166
168
|
with_scope :find => find_options do
|
167
169
|
# Doing the find like this eliminates the possibility of errors occuring
|
168
170
|
# on either missing records (out-of-sync) or an empty results array.
|
169
171
|
records = find(:all, :conditions => [ "#{table_name}.id IN (?)", part_query])
|
170
|
-
|
172
|
+
|
171
173
|
if find_options.include?(:order)
|
172
174
|
records # Just return the records without ranking them.
|
173
175
|
else
|
@@ -176,11 +178,11 @@ module ActsAsIndexed #:nodoc:
|
|
176
178
|
records.each do |r|
|
177
179
|
ranked_records[r] = @query_cache[query][r.id]
|
178
180
|
end
|
179
|
-
|
181
|
+
|
180
182
|
ranked_records.to_a.sort_by{|a| a.last }.reverse.map(&:first)
|
181
183
|
end
|
182
184
|
end
|
183
|
-
|
185
|
+
|
184
186
|
end
|
185
187
|
|
186
188
|
private
|
@@ -191,7 +193,7 @@ module ActsAsIndexed #:nodoc:
|
|
191
193
|
offset = 0
|
192
194
|
while (records = find(:all, :limit => increment, :offset => offset)).size > 0
|
193
195
|
#p "offset is #{offset}, increment is #{increment}"
|
194
|
-
index = SearchIndex.new(aai_config.index_file, aai_config.index_file_depth, aai_fields, aai_config.min_word_size)
|
196
|
+
index = SearchIndex.new(aai_config.index_file, aai_config.index_file_depth, aai_fields, aai_config.min_word_size, aai_config.if_proc)
|
195
197
|
offset += increment
|
196
198
|
index.add_records(records)
|
197
199
|
index.save
|
@@ -245,4 +247,4 @@ end
|
|
245
247
|
|
246
248
|
ActiveRecord::Base.class_eval do
|
247
249
|
include ActsAsIndexed
|
248
|
-
end
|
250
|
+
end
|
data/lib/will_paginate_search.rb
CHANGED
@@ -2,20 +2,20 @@
|
|
2
2
|
# Copyright (c) 2007 - 2010 Douglas F Shearer.
|
3
3
|
# http://douglasfshearer.com
|
4
4
|
|
5
|
-
module
|
5
|
+
module ActsAsIndexed
|
6
6
|
|
7
|
-
|
7
|
+
module WillPaginate
|
8
8
|
|
9
|
-
|
9
|
+
module Search
|
10
10
|
|
11
11
|
# DEPRECATED. Use chained pagination instead.
|
12
12
|
def paginate_search(query, options)
|
13
13
|
warn "[DEPRECATION] `paginate_search` is deprecated and will be removed in a later release. Use `with_query(query).paginate()` instead."
|
14
14
|
page, per_page, total_entries = wp_parse_options(options)
|
15
|
-
|
15
|
+
|
16
16
|
total_entries ||= find_with_index(query,{},{:ids_only => true}).size
|
17
17
|
|
18
|
-
returning WillPaginate::Collection.new(page, per_page, total_entries) do |pager|
|
18
|
+
returning ::WillPaginate::Collection.new(page, per_page, total_entries) do |pager|
|
19
19
|
options.update :offset => pager.offset, :limit => pager.per_page
|
20
20
|
|
21
21
|
options = options.delete_if {|key, value| [:page, :per_page].include?(key) }
|
@@ -27,3 +27,7 @@ module WillPaginate
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
end
|
30
|
+
|
31
|
+
class ActiveRecord::Base
|
32
|
+
extend ActsAsIndexed::WillPaginate::Search
|
33
|
+
end
|
data/test/abstract_unit.rb
CHANGED
@@ -9,44 +9,45 @@ require 'mocha'
|
|
9
9
|
# Do this before requiring AAI.
|
10
10
|
class Rails
|
11
11
|
def self.root
|
12
|
-
Dir.pwd
|
12
|
+
Pathname.new(Dir.pwd)
|
13
13
|
end
|
14
14
|
def self.env
|
15
15
|
'test'
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
-
|
19
|
+
test_path = Pathname.new(File.expand_path('../', __FILE__))
|
20
|
+
require test_path.parent.join('lib', 'acts_as_indexed').to_s
|
20
21
|
|
21
|
-
ActiveRecord::Base.logger = Logger.new(
|
22
|
-
ActiveRecord::Base.configurations = YAML::load(IO.read(
|
22
|
+
ActiveRecord::Base.logger = Logger.new(test_path.join('test.log').to_s)
|
23
|
+
ActiveRecord::Base.configurations = YAML::load(IO.read(test_path.join('database.yml').to_s))
|
23
24
|
ActiveRecord::Base.establish_connection(ENV['DB'] || 'sqlite3')
|
24
25
|
|
25
26
|
# Load Schema
|
26
|
-
load(
|
27
|
+
load(test_path.join('schema.rb').to_s)
|
27
28
|
|
28
29
|
# Load model.
|
29
|
-
$LOAD_PATH.unshift(
|
30
|
+
$LOAD_PATH.unshift(test_path.join('fixtures').to_s)
|
30
31
|
|
31
32
|
class ActiveSupport::TestCase #:nodoc:
|
32
33
|
include ActiveRecord::TestFixtures
|
33
|
-
self.fixture_path = File.
|
34
|
+
self.fixture_path = Pathname.new(File.expand_path('../', __FILE__)).join('fixtures').to_s
|
34
35
|
self.use_transactional_fixtures = true
|
35
36
|
self.use_instantiated_fixtures = false
|
36
|
-
|
37
|
+
|
37
38
|
def destroy_index
|
38
39
|
`rm -rdf #{index_loc}`
|
39
40
|
end
|
40
|
-
|
41
|
+
|
41
42
|
def build_index
|
42
43
|
# Makes a query to invoke the index build.
|
43
44
|
assert_equal [], Post.find_with_index('badger')
|
44
|
-
assert
|
45
|
+
assert index_loc.exist?
|
45
46
|
true
|
46
47
|
end
|
47
|
-
|
48
|
+
|
48
49
|
def index_loc
|
49
|
-
|
50
|
+
Rails.root.join('index')
|
50
51
|
end
|
51
|
-
|
52
|
+
|
52
53
|
end
|
@@ -1,9 +1,11 @@
|
|
1
|
-
require File.
|
1
|
+
require File.expand_path("../abstract_unit", __FILE__)
|
2
2
|
|
3
3
|
class ActsAsIndexedTest < ActiveSupport::TestCase
|
4
4
|
fixtures :posts
|
5
5
|
|
6
6
|
def teardown
|
7
|
+
# need to do this to work with the :if Proc tests.
|
8
|
+
Post.acts_as_indexed :fields => [:title, :body]
|
7
9
|
destroy_index
|
8
10
|
end
|
9
11
|
|
@@ -49,7 +51,7 @@ class ActsAsIndexedTest < ActiveSupport::TestCase
|
|
49
51
|
p = Post.create(:title => 'A special title', :body => 'foo bar bla bla bla')
|
50
52
|
assert Post.find_with_index('title',{},{:ids_only => true}).include?(p.id)
|
51
53
|
p.update_attributes(:title => 'No longer special')
|
52
|
-
assert
|
54
|
+
assert Post.find_with_index('title',{},{:ids_only => true}).exclude?(p.id)
|
53
55
|
end
|
54
56
|
|
55
57
|
def test_simple_queries
|
@@ -120,14 +122,76 @@ class ActsAsIndexedTest < ActiveSupport::TestCase
|
|
120
122
|
assert_equal all_results[1], second_result.first.id
|
121
123
|
end
|
122
124
|
|
123
|
-
# When a atom already in a record is duplicated, it removes
|
125
|
+
# When a atom already in a record is duplicated, it removes
|
124
126
|
# all records with that same atom from the index.
|
125
127
|
def test_update_record_bug
|
126
|
-
assert_equal 2, Post.find_with_index('crane',{},{:ids_only => true}).size
|
127
128
|
p = Post.find(6)
|
128
129
|
assert p.update_attributes(:body => p.body + ' crane')
|
129
130
|
assert_equal 2, Post.find_with_index('crane',{},{:ids_only => true}).size
|
130
131
|
assert_equal 2, Post.find_with_index('ship',{},{:ids_only => true}).size
|
131
132
|
end
|
132
|
-
|
133
|
+
|
134
|
+
# If an if proc is supplied, the index should only be created if the proc evaluated true
|
135
|
+
def test_create_if
|
136
|
+
Post.acts_as_indexed :fields => [:title, :body], :if => Proc.new { |post| post.visible }
|
137
|
+
|
138
|
+
original_post_count = Post.count
|
139
|
+
assert_equal [], Post.find_with_index('badger')
|
140
|
+
p = Post.new(:title => 'badger', :body => 'thousands of them!', :visible => true)
|
141
|
+
assert p.save
|
142
|
+
assert_equal original_post_count + 1, Post.count
|
143
|
+
assert_equal [p.id], Post.find_with_index('badger', {}, { :no_query_cache => true, :ids_only => true})
|
144
|
+
|
145
|
+
original_post_count = Post.count
|
146
|
+
assert_equal [], Post.find_with_index('unicorns')
|
147
|
+
p = Post.new(:title => 'unicorns', :body => 'there are none', :visible => false)
|
148
|
+
assert p.save
|
149
|
+
assert_equal original_post_count + 1, Post.count
|
150
|
+
assert_equal [], Post.find_with_index('unicorns', {}, { :no_query_cache => true, :ids_only => true})
|
151
|
+
end
|
152
|
+
|
153
|
+
# If an index already exists, and an if proc is supplied, and the proc is true, it should still appear in the index
|
154
|
+
def test_update_if_update
|
155
|
+
Post.acts_as_indexed :fields => [:title, :body], :if => Proc.new { |post| post.visible }
|
156
|
+
destroy_index
|
157
|
+
|
158
|
+
assert_equal 1, Post.find_with_index('crane', {}, { :no_query_cache => true, :ids_only => true}).size
|
159
|
+
p = Post.find(6)
|
160
|
+
assert p.update_attributes(:visible => true)
|
161
|
+
assert_equal 1, Post.find_with_index('crane', {}, { :no_query_cache => true, :ids_only => true}).size
|
162
|
+
end
|
163
|
+
|
164
|
+
# If an index already exists, and an if proc is supplied, and the proc is false, it should no longer appear in the index
|
165
|
+
def test_update_if_remove
|
166
|
+
Post.acts_as_indexed :fields => [:title, :body], :if => Proc.new { |post| post.visible }
|
167
|
+
destroy_index
|
168
|
+
|
169
|
+
assert_equal 1, Post.find_with_index('crane', {}, { :no_query_cache => true, :ids_only => true}).size
|
170
|
+
p = Post.find(6)
|
171
|
+
assert p.update_attributes(:visible => false)
|
172
|
+
assert_equal 0, Post.find_with_index('crane',{},{ :no_query_cache => true, :ids_only => true}).size
|
173
|
+
end
|
174
|
+
|
175
|
+
# If an index doesn't exist, and an if proc is supplied, and the proc is true, it should appear in the index
|
176
|
+
def test_update_if_add
|
177
|
+
Post.acts_as_indexed :fields => [:title, :body], :if => Proc.new { |post| post.visible }
|
178
|
+
destroy_index
|
179
|
+
|
180
|
+
assert_equal 1, Post.find_with_index('crane', {}, { :no_query_cache => true, :ids_only => true}).size
|
181
|
+
p = Post.find(5)
|
182
|
+
assert p.update_attributes(:visible => true)
|
183
|
+
assert_equal 2, Post.find_with_index('crane',{},{ :no_query_cache => true, :ids_only => true}).size
|
184
|
+
end
|
185
|
+
|
186
|
+
# If an index doesn't exist, and an if proc is supplied, and the proc is false, then nothing happens
|
187
|
+
def test_update_if_not_in
|
188
|
+
Post.acts_as_indexed :fields => [:title, :body], :if => Proc.new { |post| post.visible }
|
189
|
+
destroy_index
|
190
|
+
|
191
|
+
assert_equal 1, Post.find_with_index('crane', {}, { :no_query_cache => true, :ids_only => true}).size
|
192
|
+
p = Post.find(5)
|
193
|
+
assert p.update_attributes(:visible => false)
|
194
|
+
assert_equal 1, Post.find_with_index('crane',{},{ :no_query_cache => true, :ids_only => true}).size
|
195
|
+
end
|
196
|
+
|
133
197
|
end
|
data/test/configuration_test.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
|
-
require File.
|
1
|
+
require File.expand_path("../abstract_unit", __FILE__)
|
2
2
|
include ActsAsIndexed
|
3
3
|
|
4
|
-
class ConfigurationTest < ActiveSupport::TestCase
|
5
|
-
|
4
|
+
class ConfigurationTest < ActiveSupport::TestCase
|
5
|
+
|
6
6
|
def test_default_index_file_should_be_set
|
7
|
-
assert_equal
|
7
|
+
assert_equal Rails.root.join('index'), config.index_file
|
8
8
|
end
|
9
|
-
|
9
|
+
|
10
10
|
def test_default_index_file_depth_should_be_set
|
11
11
|
assert_equal 3, config.index_file_depth
|
12
12
|
end
|
@@ -14,26 +14,26 @@ class ConfigurationTest < ActiveSupport::TestCase
|
|
14
14
|
def test_default_min_word_size_should_be_set
|
15
15
|
assert_equal 3, config.min_word_size
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
def test_index_file_should_be_writeable
|
19
19
|
config.index_file = [Rails.root, 'my_index']
|
20
|
-
assert_equal
|
20
|
+
assert_equal Rails.root.join('my_index'), config.index_file
|
21
21
|
end
|
22
|
-
|
22
|
+
|
23
23
|
def test_index_file_depth_should_be_writeable
|
24
24
|
config.index_file_depth = 5
|
25
25
|
assert_equal 5, config.index_file_depth
|
26
26
|
end
|
27
|
-
|
27
|
+
|
28
28
|
def test_index_file_depth_should_raise_on_lower_than_1_value
|
29
29
|
assert_nothing_raised(ArgumentError) { config.index_file_depth = 1 }
|
30
|
-
|
30
|
+
|
31
31
|
e = assert_raise(ArgumentError) { config.index_file_depth = 0 }
|
32
32
|
assert_equal 'index_file_depth cannot be less than one (1)', e.message
|
33
|
-
|
33
|
+
|
34
34
|
assert_raise(ArgumentError) { config.index_file_depth = -12 }
|
35
35
|
end
|
36
|
-
|
36
|
+
|
37
37
|
def test_min_word_size_should_be_writeable
|
38
38
|
config.min_word_size = 7
|
39
39
|
assert_equal 7, config.min_word_size
|
@@ -47,11 +47,11 @@ class ConfigurationTest < ActiveSupport::TestCase
|
|
47
47
|
|
48
48
|
assert_raise(ArgumentError) { config.min_word_size = -12 }
|
49
49
|
end
|
50
|
-
|
50
|
+
|
51
51
|
private
|
52
|
-
|
52
|
+
|
53
53
|
def config
|
54
54
|
@config ||=ActsAsIndexed::Configuration.new
|
55
55
|
end
|
56
|
-
|
56
|
+
|
57
57
|
end
|
data/test/fixtures/posts.yml
CHANGED
@@ -4,28 +4,34 @@ wikipedia_article_1:
|
|
4
4
|
id: 1
|
5
5
|
title: Body Count (video game)
|
6
6
|
body: Body Count is a 1994 First-person shooter for the Sega Mega Drive. It is one of the few games that make use of the Menacer light gun and the Mega Mouse. \n In the U.S. the game was released on the Sega Channel.
|
7
|
-
|
7
|
+
visible: 1
|
8
|
+
|
8
9
|
wikipedia_article_2:
|
9
10
|
id: 2
|
10
11
|
title: Julien Ellis
|
11
12
|
body: Julien Ellis is a ice hockey goalie, born in Sorel, Quebec on January 27, 1986. He is currently 6'0" and weighs approximately 177 pounds. He wears number 34 and catches left. \n Julien played his entire junior hockey career in the QMJHL with the Shawinigan Cataractes. He was there from 2002 through the 2006 season, and played a total of 173 regular season games for them. During his time there, he recorded eight shutouts, as well as a career high .921 save percentage and 2.41 goals against average. \n Julien was chosen in round six of the 2004 NHL Entry Draft by the Vancouver Canucks, making him 189th overall pick and the 5th pick for Vancouver. \n His 2006-07 season was spent with the Victoria Salmon Kings of the ECHL, where he played 37 games and made 1,212 saves. Julien was called up the the Manitoba Moose of the AHL several times, where he played eight games.
|
13
|
+
visible: 1
|
12
14
|
|
13
15
|
wikipedia_article_3:
|
14
16
|
id: 3
|
15
17
|
title: Tuen Mun River
|
16
18
|
body: The Tuen Mun River is a river in Tuen Mun, New Territories, Hong Kong. It has many tributaries, with major ones coming from Lam Tei, Kau Keng Shan, Hung Shui Hang and Nai Wai. It flows south, splitting Tuen Mun into a west side and an east side. It eventually feeds into the Tuen Mun Typhoon Shelter, which is part of Castle Peak Bay.
|
19
|
+
visible: 0
|
17
20
|
|
18
21
|
wikipedia_article_4:
|
19
22
|
id: 4
|
20
23
|
title: So Happily Unsatisfied
|
21
24
|
body: So Happily Unsatisfied is an album that was recorded by the band Nine Days. It was intended to be the follow-up to their successful major-label debut, The Madding Crowd from 2000. The release date of the album was repeatedly delayed by Sony until the band was ultimately dropped. In the interim, the album had leaked onto the internet. The band has also put the whole album on their official website for the public to download.
|
25
|
+
visible: 1
|
22
26
|
|
23
27
|
wikipedia_article_5:
|
24
28
|
id: 5
|
25
29
|
title: SS Cornhusker State (T-ACS-6)
|
26
30
|
body: SS Cornhusker State (T-ACS-6) is a crane ship in ready reserve for the United States Navy. She is stationed at Cheatham Annex in Williamsburg, Virginia and is in ready reserve under the Military Sealift Command. The ship was named for the state of Nebraska, which is also known as the Cornhusker State. \n The ship was built by the Bath Iron Works. Her keel was laid on 27 November 1967, launched on 2 November 1968, and delivered 20 June 1969 as CV Stag Hound (MA 207). \n Stag Hound was acquired by the US Navy from the Maritime Administration in 1986 and was converted throughout 1987. She re-entered service as Cornhusker State on 12 March 1988, and has been in ready reserve since 1993.
|
31
|
+
visible: 0
|
27
32
|
|
28
33
|
article_similar_to_5:
|
29
34
|
id: 6
|
30
35
|
title: An article I made up by myself!
|
31
|
-
body: crane crane big ship foo
|
36
|
+
body: crane crane big ship foo
|
37
|
+
visible: 1
|
data/test/schema.rb
CHANGED
data/test/search_atom_test.rb
CHANGED
data/test/search_index_test.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: acts_as_indexed
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 1
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 6
|
9
|
-
-
|
10
|
-
version: 0.6.
|
9
|
+
- 3
|
10
|
+
version: 0.6.3
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Douglas F Shearer
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-
|
18
|
+
date: 2010-07-05 00:00:00 +01:00
|
19
19
|
default_executable:
|
20
20
|
dependencies: []
|
21
21
|
|