search_do 0.2.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/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
|