search_do 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +1 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +63 -0
- data/Rakefile +47 -0
- data/TESTING +6 -0
- data/VERSION +1 -0
- data/examples/he_search.rb +13 -0
- data/examples/person.rb +20 -0
- data/init.rb +1 -0
- data/lib/estraier_admin.rb +47 -0
- data/lib/search_do/backends/hyper_estraier/estraier_pure_extention.rb +61 -0
- data/lib/search_do/backends/hyper_estraier.rb +213 -0
- data/lib/search_do/backends.rb +17 -0
- data/lib/search_do/dirty_tracking/bridge.rb +22 -0
- data/lib/search_do/dirty_tracking/self_made.rb +36 -0
- data/lib/search_do/dirty_tracking.rb +15 -0
- data/lib/search_do/indexer.rb +65 -0
- data/lib/search_do/utils.rb +11 -0
- data/lib/search_do.rb +330 -0
- data/lib/vendor/estraierpure.rb +1025 -0
- data/lib/vendor/overview +100 -0
- data/recipes/mode_maintenance.rb +52 -0
- data/spec/backends/hyper_estraier_spec.rb +220 -0
- data/spec/backends/result_document_spec.rb +26 -0
- data/spec/dirty_tracking/bridge_spec.rb +33 -0
- data/spec/estraier_admin_spec.rb +26 -0
- data/spec/fixtures/stories.yml +27 -0
- data/spec/indexer_spec.rb +59 -0
- data/spec/search_do_spec.rb +335 -0
- data/spec/setup_test_model.rb +38 -0
- data/spec/spec_helper.rb +52 -0
- data/tasks/acts_as_searchable_tasks.rake +70 -0
- metadata +95 -0
@@ -0,0 +1,335 @@
|
|
1
|
+
require File.expand_path("spec_helper", File.dirname(__FILE__))
|
2
|
+
|
3
|
+
require 'digest/sha1'
|
4
|
+
|
5
|
+
describe Story, "extended by acts_as_searchable_enhance" do
|
6
|
+
it "should respond_to :fulltext_search" do
|
7
|
+
Story.should respond_to(:fulltext_search)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should have callbak :update_index" do
|
11
|
+
Story.after_update.should include(:update_index)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should have callbak :add_to_index" do
|
15
|
+
Story.after_create.should include(:add_to_index)
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "separate node by model classname" do
|
19
|
+
before(:all) do
|
20
|
+
OtherKlass = Class.new(ActiveRecord::Base)
|
21
|
+
OtherKlass.class_eval do
|
22
|
+
acts_as_searchable :ignore_timestamp => true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
it "OtherKlass.search_backend.node_name.should == 'aas_e_test_other_klasses'" do
|
27
|
+
OtherKlass.search_backend.node_name.should == 'aas_e_test_other_klasses'
|
28
|
+
end
|
29
|
+
|
30
|
+
it "search_backend.node_name should == 'aas_e_test_stories'" do
|
31
|
+
Story.search_backend.node_name.should == 'aas_e_test_stories'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "matched_ids" do
|
36
|
+
fixtures :stories
|
37
|
+
before do
|
38
|
+
@story_ids = Story.find(:all, :limit=>2).map(&:id)
|
39
|
+
|
40
|
+
@mock_results = @story_ids.map{|id| mock("ResultDocument_#{id}", :attr => id) }
|
41
|
+
nres = EstraierPure::NodeResult.new(@mock_results, {})
|
42
|
+
Story.search_backend.connection.stub!(:search).and_return(nres)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "finds all story ids" do
|
46
|
+
Story.matched_ids("hoge").should == @story_ids
|
47
|
+
end
|
48
|
+
|
49
|
+
it "calls EstraierPure::Node#search()" do
|
50
|
+
Story.search_backend.connection.should_receive(:search)
|
51
|
+
Story.matched_ids("hoge")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "remove from index" do
|
56
|
+
fixtures :stories
|
57
|
+
before do
|
58
|
+
stories = Story.find(:all, :limit=>2)
|
59
|
+
@story = stories.first
|
60
|
+
mock_results = stories.map{|s| mock("ResultDocument_#{s.id}", :attr => s.id) }
|
61
|
+
|
62
|
+
nres = EstraierPure::NodeResult.new(mock_results, {})
|
63
|
+
Story.search_backend.connection.stub!(:search).and_return(nres)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should call EstraierPure::Node#delete_from_index" do
|
67
|
+
Story.search_backend.connection.should_receive(:out_doc)
|
68
|
+
@story.remove_from_index
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "add_snippets" do
|
73
|
+
before :each do
|
74
|
+
@stories = Story.find :all
|
75
|
+
count = 0
|
76
|
+
@ids_and_raw = @stories.map {|story| [story.id,fake_raw(count+=1)]}
|
77
|
+
end
|
78
|
+
|
79
|
+
it "adds snippets to objects" do
|
80
|
+
Story.send(:add_snippets,@stories,@ids_and_raw)
|
81
|
+
@stories.map(&:snippet).should == ['snip1','snip2']
|
82
|
+
end
|
83
|
+
|
84
|
+
it "adds snippets to right objects" do
|
85
|
+
Story.send(:add_snippets,@stories.reverse,@ids_and_raw)
|
86
|
+
@stories.map(&:snippet).should == ['snip1','snip2']
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe :snippet_to_html do
|
91
|
+
it "surrounds snippets with bold" do
|
92
|
+
Story.send(:snippet_to_html,[['x',true],['y',false]]).should == "<b>x</b>y"
|
93
|
+
end
|
94
|
+
|
95
|
+
it "removes tags html" do#since they would get broken up
|
96
|
+
Story.send(:snippet_to_html,[['ab</b>ab',true],['y',false]]).should == "<b>abab</b>y"
|
97
|
+
Story.send(:snippet_to_html,[['ab<br>ab',true],['y',false]]).should == "<b>abab</b>y"
|
98
|
+
end
|
99
|
+
|
100
|
+
it "strips tags" do
|
101
|
+
Story.send(:snippet_to_html,[['ab<a>x</a>ab',true],['y',false]]).should == "<b>abxab</b>y"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe "fulltext_search" do
|
106
|
+
#matched_ids_and_raw => [:id,raw] and find_option=>{:condition => 'id = :id'}
|
107
|
+
|
108
|
+
fixtures :stories
|
109
|
+
before do
|
110
|
+
stories = Story.find(:all)
|
111
|
+
@story = stories.first
|
112
|
+
fake_results = stories.map{|story| [story.id,fake_raw]}
|
113
|
+
Story.stub!(:matched_ids_and_raw).and_return fake_results
|
114
|
+
end
|
115
|
+
|
116
|
+
def fulltext_search(query='hoge')
|
117
|
+
finder_opt = {:conditions => ["id = ?", @story.id]}
|
118
|
+
Story.fulltext_search(query, :find => finder_opt)
|
119
|
+
end
|
120
|
+
|
121
|
+
it "finds story" do
|
122
|
+
fulltext_search.should == [@story]
|
123
|
+
end
|
124
|
+
|
125
|
+
it "adds snippets" do
|
126
|
+
fulltext_search[0].snippet.should == 'snip'
|
127
|
+
end
|
128
|
+
|
129
|
+
it "does not add snippets if query is empty" do
|
130
|
+
fulltext_search('')[0].snippet.should be_nil
|
131
|
+
end
|
132
|
+
|
133
|
+
it "does not add snippet when object does not respond to snippet=" do
|
134
|
+
@story.stub!(:respond_to?).with(:html_snippet=)
|
135
|
+
@story.should_receive(:respond_to?).with(:snippet=).and_return false
|
136
|
+
Story.should_receive(:find).and_return [@story]
|
137
|
+
fulltext_search[0].snippet.should be_blank
|
138
|
+
end
|
139
|
+
|
140
|
+
it "calls matched_ids_and_raw" do
|
141
|
+
Story.should_receive(:matched_ids_and_raw).and_return([[@story.id,fake_raw]])
|
142
|
+
fulltext_search
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
describe "paginate_by_fulltext_search" do
|
147
|
+
before do
|
148
|
+
Story.stub!(:fulltext_search).and_return [Story.first]
|
149
|
+
Story.stub!(:count_fulltext).and_return 1
|
150
|
+
end
|
151
|
+
|
152
|
+
it "has total_entries" do
|
153
|
+
Story.paginate_by_fulltext_search('',:page=>1,:per_page=>1).total_entries.should == 1
|
154
|
+
end
|
155
|
+
|
156
|
+
it "translates paginate terms to limit and offset and removes page/per_page" do
|
157
|
+
Story.should_receive(:fulltext_search).with('x',{:limit=>3,:offset=>6}).and_return []
|
158
|
+
Story.paginate_by_fulltext_search('x',:page=>3,:per_page=>3)
|
159
|
+
end
|
160
|
+
|
161
|
+
it "uses search word and attributes for count query" do
|
162
|
+
Story.should_receive(:count_fulltext).with('x',:attributes=>'something').and_return 1
|
163
|
+
Story.paginate_by_fulltext_search('x',:page=>1,:per_page=>1,:attributes=>'something')
|
164
|
+
end
|
165
|
+
|
166
|
+
it "calculates total_entries from search results" do
|
167
|
+
Story.should_receive(:count_fulltext).never
|
168
|
+
Story.paginate_by_fulltext_search('',:page=>1,:per_page=>2).total_entries.should == 1
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
describe "new interface Model.find_fulltext(query, options={})" do
|
173
|
+
fixtures :stories
|
174
|
+
|
175
|
+
before do
|
176
|
+
Story.stub!(:matched_ids).and_return([102, 101, 110])
|
177
|
+
end
|
178
|
+
|
179
|
+
it "find_fulltext('hoge', :order=>'updated_at DESC') should == [stories(:sanshiro), stories(:neko)]" do
|
180
|
+
Story.find_fulltext('hoge', :order=>"updated_at DESC").should == Story.find([102,101]).reverse
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
describe "search using real HyperEstraier (End-to-End test)" do
|
185
|
+
fixtures :stories
|
186
|
+
before(:all) do
|
187
|
+
Story.clear_index!
|
188
|
+
#Story.delete_all
|
189
|
+
Story.create!(:title=>"むかしむかし", :body=>"あるところにおじいさんとおばあさんが")
|
190
|
+
Story.create!(:title=>"i am so blue", :body=>"testing makes me happy",:non_column=>'non column value')
|
191
|
+
Story.reindex!
|
192
|
+
# waiting Estraier sync index, adjust 'cachernum' in ${estraier}/_conf if need
|
193
|
+
sleep 1
|
194
|
+
end
|
195
|
+
|
196
|
+
before do
|
197
|
+
@story = Story.find_by_title("むかしむかし")
|
198
|
+
end
|
199
|
+
|
200
|
+
it "finds a indexed object" do
|
201
|
+
Story.fulltext_search('むかしむかし').should == [@story]
|
202
|
+
end
|
203
|
+
|
204
|
+
it "counts correctly using count_fulltext" do
|
205
|
+
Story.count_fulltext('むかしむかし').should == 1
|
206
|
+
end
|
207
|
+
|
208
|
+
it "finds all object when searching for ''" do
|
209
|
+
Story.fulltext_search('').size.should == Story.count
|
210
|
+
end
|
211
|
+
|
212
|
+
it "finds using long strings" do
|
213
|
+
Story.fulltext_search("i am so blue").size.should == 1
|
214
|
+
end
|
215
|
+
|
216
|
+
it "finds using attributes" do
|
217
|
+
#FIXME this fails if iSTRAND is used, why?
|
218
|
+
Story.fulltext_search('',:attributes=>{:body=>'るとこ'}).size.should == 1
|
219
|
+
end
|
220
|
+
|
221
|
+
it "finds using long strings in attributes" do
|
222
|
+
Story.fulltext_search('',:attributes=>{:body=>'testing makes'}).size.should == 1
|
223
|
+
end
|
224
|
+
|
225
|
+
it "finds using long strings using and in attributes" do
|
226
|
+
pending do
|
227
|
+
#FIXME this works if iSTRAND is used, but then "finds using attributes" fails
|
228
|
+
Story.fulltext_search('',:attributes=>{:body=>'testing makes happy'}).size.should == 1
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
it "finds using upper or lowercase attributes" do
|
233
|
+
Story.fulltext_search('',:attributes=>{:body=>'MakeS'}).size.should == 1
|
234
|
+
end
|
235
|
+
|
236
|
+
it "finds using non_column attributes" do
|
237
|
+
pending do
|
238
|
+
Story.fulltext_search('',:attributes=>{:non_column=>'non column value'}).size.should == 1
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
# asserts HE raw_match order
|
243
|
+
it "finds in correct order(descending)" do
|
244
|
+
Story.matched_ids('記憶', :order => "@mdate NUMD").should == [101, 102]
|
245
|
+
end
|
246
|
+
|
247
|
+
it "finds in correct order(ascending)" do
|
248
|
+
Story.matched_ids('記憶', :order => "@mdate NUMA").should == [102, 101]
|
249
|
+
end
|
250
|
+
|
251
|
+
it "preserves order of found objects" do
|
252
|
+
Story.fulltext_search('記憶', :order => "@mdate NUMA").map(&:id).should == [102, 101]
|
253
|
+
end
|
254
|
+
|
255
|
+
it "preservers order if scope is given" do
|
256
|
+
pending
|
257
|
+
end
|
258
|
+
|
259
|
+
it "has all objects in index" do
|
260
|
+
Story.search_backend.index.size.should == Story.count
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
describe "partial updating" do
|
265
|
+
fixtures :stories
|
266
|
+
before do
|
267
|
+
@story = Story.find(:first)
|
268
|
+
@story.stub!(:record_timestamps).and_return(false)
|
269
|
+
end
|
270
|
+
|
271
|
+
it "updates when changing indexed column" do
|
272
|
+
Story.search_backend.should_receive(:add_to_index).once
|
273
|
+
@story.title = "new title"
|
274
|
+
@story.save
|
275
|
+
end
|
276
|
+
|
277
|
+
it "does not updates when changing unindexed column" do
|
278
|
+
Story.search_backend.should_not_receive(:add_to_index)
|
279
|
+
@story.popularity += 10
|
280
|
+
@story.save
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
def fake_raw(num=nil)
|
285
|
+
mock("Raw",:snippet=>"snip#{num}")
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
describe "StoryWithoutAutoUpdate" do
|
290
|
+
before(:all) do
|
291
|
+
class StoryWithoutAutoUpdate < ActiveRecord::Base
|
292
|
+
set_table_name :stories
|
293
|
+
acts_as_searchable :searchable_fields=>[:title, :body], :auto_update=>false
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
it "should not have callback :update_index" do
|
298
|
+
StoryWithoutAutoUpdate.after_update.should_not include(:update_index)
|
299
|
+
end
|
300
|
+
|
301
|
+
it "should not have callback :add_to_index" do
|
302
|
+
StoryWithoutAutoUpdate.after_create.should_not include(:add_to_index)
|
303
|
+
end
|
304
|
+
|
305
|
+
it "should not call add_to_index" do
|
306
|
+
story = StoryWithoutAutoUpdate.new
|
307
|
+
StoryWithoutAutoUpdate.search_backend.should_not_receive(:add_to_index)
|
308
|
+
story.popularity = 20
|
309
|
+
story.save
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
describe SearchDo::Utils do
|
314
|
+
describe "tokenize_query" do
|
315
|
+
it "converts nil to empty string" do
|
316
|
+
SearchDo::Utils.cleanup_query(nil).should == ''
|
317
|
+
end
|
318
|
+
|
319
|
+
it "does not convert empty strings to nil" do
|
320
|
+
SearchDo::Utils.cleanup_query('').should == ''
|
321
|
+
end
|
322
|
+
|
323
|
+
it "does not alter search terms'" do
|
324
|
+
SearchDo::Utils.cleanup_query('ruby vim').should == 'ruby vim'
|
325
|
+
end
|
326
|
+
|
327
|
+
it "leaves in quotes" do
|
328
|
+
SearchDo::Utils.cleanup_query('"ruby on rails" vim').should == '"ruby on rails" vim'
|
329
|
+
end
|
330
|
+
|
331
|
+
it "converts long unicode spaces" do
|
332
|
+
SearchDo::Utils.cleanup_query('rails vim').should == 'rails vim'
|
333
|
+
end
|
334
|
+
end
|
335
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'active_record'
|
3
|
+
require 'active_record/fixtures'
|
4
|
+
|
5
|
+
#activate will_paginate for models
|
6
|
+
require 'will_paginate'
|
7
|
+
WillPaginate.enable_activerecord
|
8
|
+
|
9
|
+
#create model table
|
10
|
+
ActiveRecord::Schema.define(:version => 1) do
|
11
|
+
create_table "stories" do |t|
|
12
|
+
t.date "written_on"
|
13
|
+
t.datetime "read_at"
|
14
|
+
t.string "title"
|
15
|
+
t.text "body"
|
16
|
+
t.integer "popularity", :default =>0
|
17
|
+
t.timestamps
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
#create model
|
22
|
+
class Story < ActiveRecord::Base
|
23
|
+
attr_accessor :snippet
|
24
|
+
acts_as_searchable :searchable_fields=>[:title, :body],:attributes=>{:non_column=>nil,:body=>nil}
|
25
|
+
|
26
|
+
def non_column
|
27
|
+
@non_column
|
28
|
+
end
|
29
|
+
|
30
|
+
def non_column=(val)
|
31
|
+
@non_column=val
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
#create node
|
36
|
+
require File.expand_path("../lib/estraier_admin", File.dirname(__FILE__))
|
37
|
+
admin = EstraierAdmin.new(ActiveRecord::Base.configurations["test"][:estraier])
|
38
|
+
admin.create_node(Story.search_backend.node_name)
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# ---- requirements
|
2
|
+
$KCODE = 'u' #activate regex unicode
|
3
|
+
require 'rubygems'
|
4
|
+
require 'spec'
|
5
|
+
$LOAD_PATH << File.expand_path("../lib", File.dirname(__FILE__))
|
6
|
+
|
7
|
+
|
8
|
+
# ---- bugfix
|
9
|
+
#`exit?': undefined method `run?' for Test::Unit:Module (NoMethodError)
|
10
|
+
#can be solved with require test/unit but this will result in extra test-output
|
11
|
+
unless defined? Test::Unit
|
12
|
+
module Test
|
13
|
+
module Unit
|
14
|
+
def self.run?
|
15
|
+
true
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
# ---- load active record
|
23
|
+
#gem 'activerecord', '2.0.2'
|
24
|
+
if ENV["AR"]
|
25
|
+
gem 'activerecord', ENV["AR"]
|
26
|
+
$stderr.puts("Using ActiveRecord #{ENV["AR"]}")
|
27
|
+
end
|
28
|
+
require 'active_record'
|
29
|
+
|
30
|
+
require File.expand_path("../init", File.dirname(__FILE__))
|
31
|
+
|
32
|
+
RAILS_ENV = "test"
|
33
|
+
ActiveRecord::Base.configurations = {"test" => {
|
34
|
+
:adapter => "sqlite3",
|
35
|
+
:database => ":memory:",
|
36
|
+
:estraier => {:host=> "localhost", :node=>"aas_e_test", :user=>"admin", :password=>"admin"}
|
37
|
+
}.with_indifferent_access}
|
38
|
+
|
39
|
+
ActiveRecord::Base.logger = Logger.new(File.directory?("log") ? "log/#{RAILS_ENV}.log" : "/dev/null")
|
40
|
+
ActiveRecord::Base.establish_connection(:test)
|
41
|
+
|
42
|
+
load File.expand_path("setup_test_model.rb", File.dirname(__FILE__))
|
43
|
+
|
44
|
+
|
45
|
+
# ---- fixtures
|
46
|
+
Spec::Example::ExampleGroupMethods.module_eval do
|
47
|
+
def fixtures(*tables)
|
48
|
+
dir = File.expand_path("fixtures", File.dirname(__FILE__))
|
49
|
+
tables.each{|table| Fixtures.create_fixtures(dir, table.to_s) }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require File.expand_path("../lib/estraier_admin", File.dirname(__FILE__))
|
2
|
+
|
3
|
+
namespace :search do
|
4
|
+
desc "Clears HE Index"
|
5
|
+
task :clear => :environment do
|
6
|
+
raise "Pass a searchable model with MODEL=" unless ENV['MODEL']
|
7
|
+
ENV['MODEL'].constantize.clear_index!
|
8
|
+
end
|
9
|
+
|
10
|
+
desc "Reindexes all model attributes"
|
11
|
+
task :reindex => :environment do
|
12
|
+
raise "Pass a searchable model with MODEL=" unless ENV['MODEL']
|
13
|
+
model_class = ENV['MODEL'].constantize
|
14
|
+
reindex = lambda { model_class.reindex! }
|
15
|
+
if ENV['INCLUDE']
|
16
|
+
model_class.with_scope :find => { :include => ENV['INCLUDE'].split(',').collect { |i| i.strip.to_sym } } do
|
17
|
+
reindex.call
|
18
|
+
end
|
19
|
+
else
|
20
|
+
reindex.call
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
namespace :node do
|
25
|
+
desc "Create HE node"
|
26
|
+
task :create => :environment do
|
27
|
+
raise "Pass a searchable model with MODEL=" unless ENV['MODEL']
|
28
|
+
model_class = ENV['MODEL'].constantize
|
29
|
+
admin.create_node(model_class.search_backend.node_name)
|
30
|
+
end
|
31
|
+
|
32
|
+
desc "Delete HE node"
|
33
|
+
task :delete => :environment do
|
34
|
+
raise "Pass a searchable model with MODEL=" unless ENV['MODEL']
|
35
|
+
model_class = ENV['MODEL'].constantize
|
36
|
+
admin.delete_node(model_class.search_backend.node_name)
|
37
|
+
end
|
38
|
+
|
39
|
+
def admin
|
40
|
+
EstraierAdmin.new(ActiveRecord::Base.configurations[RAILS_ENV]["estraier"])
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
namespace :server do
|
45
|
+
desc "initialize HyperEstraier index directory on localhost"
|
46
|
+
task :init do
|
47
|
+
system(estmaster, 'init', index_dir) unless File.directory?(index_dir)
|
48
|
+
end
|
49
|
+
|
50
|
+
desc "start HyperEstraier server on loaclhost"
|
51
|
+
task :start => [:init] do
|
52
|
+
$stderr.puts "starting HyperEstraier server..."
|
53
|
+
system(estmaster, 'start', '-bg', index_dir)
|
54
|
+
end
|
55
|
+
|
56
|
+
desc "stop HyperEstraier server on localhost"
|
57
|
+
task :stop => [:init] do
|
58
|
+
$stderr.puts "stopping HyperEstraier server..."
|
59
|
+
system(estmaster, 'stop', index_dir)
|
60
|
+
end
|
61
|
+
|
62
|
+
def estmaster
|
63
|
+
ENV['COMMAND'] || 'estmaster'
|
64
|
+
end
|
65
|
+
|
66
|
+
def index_dir
|
67
|
+
ENV['SEARCH_INDEX_DIR'] || File.expand_path("tmp/fulltext_index", Dir.pwd)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
metadata
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: search_do
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- MOROHASHI Kyosuke
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-12-24 00:00:00 +01:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: moronatural@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README.rdoc
|
24
|
+
files:
|
25
|
+
- CHANGELOG
|
26
|
+
- MIT-LICENSE
|
27
|
+
- README.rdoc
|
28
|
+
- Rakefile
|
29
|
+
- TESTING
|
30
|
+
- VERSION
|
31
|
+
- examples/he_search.rb
|
32
|
+
- examples/person.rb
|
33
|
+
- init.rb
|
34
|
+
- lib/estraier_admin.rb
|
35
|
+
- lib/search_do.rb
|
36
|
+
- lib/search_do/backends.rb
|
37
|
+
- lib/search_do/backends/hyper_estraier.rb
|
38
|
+
- lib/search_do/backends/hyper_estraier/estraier_pure_extention.rb
|
39
|
+
- lib/search_do/dirty_tracking.rb
|
40
|
+
- lib/search_do/dirty_tracking/bridge.rb
|
41
|
+
- lib/search_do/dirty_tracking/self_made.rb
|
42
|
+
- lib/search_do/indexer.rb
|
43
|
+
- lib/search_do/utils.rb
|
44
|
+
- lib/vendor/estraierpure.rb
|
45
|
+
- lib/vendor/overview
|
46
|
+
- recipes/mode_maintenance.rb
|
47
|
+
- spec/backends/hyper_estraier_spec.rb
|
48
|
+
- spec/backends/result_document_spec.rb
|
49
|
+
- spec/dirty_tracking/bridge_spec.rb
|
50
|
+
- spec/estraier_admin_spec.rb
|
51
|
+
- spec/fixtures/stories.yml
|
52
|
+
- spec/indexer_spec.rb
|
53
|
+
- spec/search_do_spec.rb
|
54
|
+
- spec/setup_test_model.rb
|
55
|
+
- spec/spec_helper.rb
|
56
|
+
- tasks/acts_as_searchable_tasks.rake
|
57
|
+
has_rdoc: true
|
58
|
+
homepage: http://github.com/grosser/search_do
|
59
|
+
licenses: []
|
60
|
+
|
61
|
+
post_install_message:
|
62
|
+
rdoc_options:
|
63
|
+
- --charset=UTF-8
|
64
|
+
require_paths:
|
65
|
+
- lib
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: "0"
|
71
|
+
version:
|
72
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: "0"
|
77
|
+
version:
|
78
|
+
requirements: []
|
79
|
+
|
80
|
+
rubyforge_project:
|
81
|
+
rubygems_version: 1.3.5
|
82
|
+
signing_key:
|
83
|
+
specification_version: 3
|
84
|
+
summary: "AR: Hyperestraier integration"
|
85
|
+
test_files:
|
86
|
+
- spec/spec_helper.rb
|
87
|
+
- spec/backends/result_document_spec.rb
|
88
|
+
- spec/backends/hyper_estraier_spec.rb
|
89
|
+
- spec/indexer_spec.rb
|
90
|
+
- spec/setup_test_model.rb
|
91
|
+
- spec/estraier_admin_spec.rb
|
92
|
+
- spec/search_do_spec.rb
|
93
|
+
- spec/dirty_tracking/bridge_spec.rb
|
94
|
+
- examples/person.rb
|
95
|
+
- examples/he_search.rb
|