keyword_search 1.3.1 → 1.4.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +4 -0
- data/{Manifest.txt → Manifest} +5 -5
- data/README.markdown +125 -0
- data/Rakefile +27 -12
- data/VERSION +1 -0
- data/keyword_search.gemspec +49 -0
- data/lib/keyword_search.rb +1107 -73
- data/lib/keyword_search.rl +41 -14
- data/lib/keyword_search/definition.rb +16 -5
- data/test/test_keyword_search.rb +162 -34
- metadata +51 -48
- data/README.txt +0 -91
data/lib/keyword_search.rl
CHANGED
@@ -15,7 +15,7 @@ module KeywordSearch
|
|
15
15
|
}
|
16
16
|
|
17
17
|
action key {
|
18
|
-
key =
|
18
|
+
key = word
|
19
19
|
results[key] ||= []
|
20
20
|
}
|
21
21
|
|
@@ -23,28 +23,53 @@ module KeywordSearch
|
|
23
23
|
key = nil
|
24
24
|
}
|
25
25
|
|
26
|
+
action word {
|
27
|
+
word = data[tokstart..p-1]
|
28
|
+
}
|
29
|
+
|
26
30
|
action value {
|
27
|
-
|
28
|
-
|
29
|
-
|
31
|
+
(results[key || :default] ||= []) << [ word, positive_match ]
|
32
|
+
}
|
33
|
+
|
34
|
+
action negative_match {
|
35
|
+
positive_match = false
|
36
|
+
}
|
37
|
+
|
38
|
+
action positive_match {
|
39
|
+
positive_match = true
|
30
40
|
}
|
31
41
|
|
32
42
|
action quote { quotes += 1 }
|
33
43
|
|
34
44
|
action unquote { quotes -= 1 }
|
35
|
-
|
36
|
-
bareword = [^ '":] [^ ":]*; # allow apostrophes
|
37
|
-
dquoted = '"' @ quote any* :>> '"' @ unquote;
|
38
|
-
squoted = '\'' @ quote any* :>> '\'' @ unquote;
|
39
45
|
|
40
|
-
|
46
|
+
seperators = ' '+ | / *[,|] */ ;
|
47
|
+
|
48
|
+
bareword = ( [^ '"(:] . [^ "):]* ) > start % word ; # allow apostrophes
|
49
|
+
dquoted = '"' @ quote ( [^"]* > start % word ) :>> '"' @ unquote;
|
50
|
+
squoted = '\'' @ quote ( [^']* > start % word ) :>> '\'' @ unquote;
|
51
|
+
|
52
|
+
anyword = dquoted | squoted | bareword ;
|
53
|
+
|
54
|
+
anyvalue = anyword % value ;
|
55
|
+
multivalues = anyvalue ( seperators anyvalue )* ;
|
56
|
+
groupedvalues = '(' @ quote multivalues :>> ')' @ unquote;
|
57
|
+
|
58
|
+
value = groupedvalues | anyvalue ;
|
59
|
+
|
60
|
+
pair = bareword % key ':' value ;
|
61
|
+
|
62
|
+
value_only = value > default ;
|
41
63
|
|
42
|
-
|
64
|
+
match_mode = ('-' % negative_match | '+'? % positive_match ) ;
|
65
|
+
|
66
|
+
definition = match_mode? <: ( pair | value_only ) ;
|
43
67
|
|
44
|
-
|
45
|
-
|
68
|
+
definitions = definition ( ' '+ definition )*;
|
69
|
+
|
70
|
+
main := ' '* definitions? ' '* 0
|
46
71
|
@!{ raise ParseError, "At offset #{p}, near: '#{data[p,10]}'" };
|
47
|
-
|
72
|
+
|
48
73
|
}%%
|
49
74
|
|
50
75
|
def search(input_string, definition=nil, &block)
|
@@ -64,14 +89,16 @@ module KeywordSearch
|
|
64
89
|
data = input + ' '
|
65
90
|
%% write data;
|
66
91
|
p = 0
|
92
|
+
eof = nil
|
93
|
+
word = nil
|
67
94
|
pe = data.length
|
68
95
|
key = nil
|
96
|
+
positive_match = nil
|
69
97
|
tokstart = nil
|
70
98
|
results = {}
|
71
99
|
quotes = 0
|
72
100
|
%% write init;
|
73
101
|
%% write exec;
|
74
|
-
%% write eof;
|
75
102
|
unless quotes.zero?
|
76
103
|
raise ParseError, "Unclosed quotes"
|
77
104
|
end
|
@@ -10,8 +10,14 @@ module KeywordSearch
|
|
10
10
|
@handler = handler
|
11
11
|
end
|
12
12
|
|
13
|
-
def handle(value)
|
14
|
-
handler
|
13
|
+
def handle(value, sign)
|
14
|
+
# If the handler is only expecting one argument,
|
15
|
+
# only give them the positive matches
|
16
|
+
if handler.arity == 1
|
17
|
+
handler.call(value) if sign
|
18
|
+
else
|
19
|
+
handler.call(value, sign)
|
20
|
+
end
|
15
21
|
end
|
16
22
|
|
17
23
|
end
|
@@ -36,11 +42,16 @@ module KeywordSearch
|
|
36
42
|
def handle(key, values)
|
37
43
|
key = @default_keyword if key == :default
|
38
44
|
return false unless key
|
45
|
+
true_values, false_values = *values.partition { |v| v[1] }
|
46
|
+
|
47
|
+
# Get just the values
|
48
|
+
true_values.collect! { |v| v[0] }
|
49
|
+
false_values.collect! { |v| v[0] }
|
50
|
+
|
39
51
|
if k = keywords.detect { |kw| kw.name == key.to_sym}
|
40
|
-
k.handle(
|
52
|
+
k.handle(true_values, true) unless true_values.empty?
|
53
|
+
k.handle(false_values, false) unless false_values.empty?
|
41
54
|
end
|
42
55
|
end
|
43
|
-
|
44
56
|
end
|
45
|
-
|
46
57
|
end
|
data/test/test_keyword_search.rb
CHANGED
@@ -4,16 +4,20 @@ require 'rubygems' rescue nil
|
|
4
4
|
require 'test/spec'
|
5
5
|
|
6
6
|
require File.dirname(__FILE__) + '/../lib/keyword_search'
|
7
|
-
|
7
|
+
|
8
8
|
context "KeywordSearch" do
|
9
|
-
|
9
|
+
|
10
10
|
NAME_AND_AGE = %<bruce williams age:26>
|
11
11
|
NAME_QUOTED_AND_AGE = %<"bruce williams" age:26>
|
12
|
-
NAME_AND_QUOTED_AGE = %<bruce williams age:"26">
|
12
|
+
NAME_AND_QUOTED_AGE = %<bruce williams age:"26">
|
13
13
|
DEFAULT_AGE_WITH_QUOTED_AGE = %<26 name:"bruce williams">
|
14
14
|
DEFAULT_AGE_WITH_SINGLE_QUOTED_AGE = %<26 name:'bruce williams'>
|
15
15
|
NAME_WITH_NESTED_SINGLE_QUOTES = %<"d'arcy d'uberville" age:28>
|
16
|
-
|
16
|
+
NAME_AND_GROUPED_AGE = %<coda hale age:(27)>
|
17
|
+
NAME_AND_GROUPED_QUOTED_AGE = %<coda hale age:("27")>
|
18
|
+
NAME_AND_GROUPED_QUOTED_AGES = %<coda hale age:("27" 34 '48')>
|
19
|
+
GROUPED_NAMES_AND_AGE = %<(coda bruce 'hale' "williams") age:20>
|
20
|
+
|
17
21
|
specify "default keyword" do
|
18
22
|
result = nil
|
19
23
|
KeywordSearch.search(NAME_AND_AGE) do |with|
|
@@ -24,7 +28,18 @@ context "KeywordSearch" do
|
|
24
28
|
end
|
25
29
|
assert_equal 'bruce williams', result
|
26
30
|
end
|
27
|
-
|
31
|
+
|
32
|
+
specify "grouped default keywords" do
|
33
|
+
result = nil
|
34
|
+
KeywordSearch.search(GROUPED_NAMES_AND_AGE) do |with|
|
35
|
+
with.default_keyword :name
|
36
|
+
with.keyword :name do |values|
|
37
|
+
result = values
|
38
|
+
end
|
39
|
+
end
|
40
|
+
assert_equal ['coda', 'bruce', 'hale', 'williams'], result
|
41
|
+
end
|
42
|
+
|
28
43
|
specify "unquoted keyword term" do
|
29
44
|
result = nil
|
30
45
|
KeywordSearch.search(NAME_AND_AGE) do |with|
|
@@ -32,9 +47,39 @@ context "KeywordSearch" do
|
|
32
47
|
result = Integer(values.first)
|
33
48
|
end
|
34
49
|
end
|
35
|
-
assert_equal 26, result
|
50
|
+
assert_equal 26, result
|
51
|
+
end
|
52
|
+
|
53
|
+
specify "unquoted grouped keyword term" do
|
54
|
+
result = nil
|
55
|
+
KeywordSearch.search(NAME_AND_GROUPED_AGE) do |with|
|
56
|
+
with.keyword :age do |values|
|
57
|
+
result = Integer(values.first)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
assert_equal 27, result
|
61
|
+
end
|
62
|
+
|
63
|
+
specify "quoted grouped keyword term" do
|
64
|
+
result = nil
|
65
|
+
KeywordSearch.search(NAME_AND_GROUPED_QUOTED_AGE) do |with|
|
66
|
+
with.keyword :age do |values|
|
67
|
+
result = Integer(values.first)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
assert_equal 27, result
|
36
71
|
end
|
37
|
-
|
72
|
+
|
73
|
+
specify "mixed grouped keyword terms" do
|
74
|
+
result = nil
|
75
|
+
KeywordSearch.search(NAME_AND_GROUPED_QUOTED_AGES) do |with|
|
76
|
+
with.keyword :age do |values|
|
77
|
+
result = values.map { |v| v.to_i }
|
78
|
+
end
|
79
|
+
end
|
80
|
+
assert_equal [27, 34, 48], result
|
81
|
+
end
|
82
|
+
|
38
83
|
specify "quoted default keyword term" do
|
39
84
|
result = nil
|
40
85
|
KeywordSearch.search(NAME_QUOTED_AND_AGE) do |with|
|
@@ -43,9 +88,9 @@ context "KeywordSearch" do
|
|
43
88
|
result = values.join(' ')
|
44
89
|
end
|
45
90
|
end
|
46
|
-
assert_equal 'bruce williams', result
|
91
|
+
assert_equal 'bruce williams', result
|
47
92
|
end
|
48
|
-
|
93
|
+
|
49
94
|
specify "quoted keyword term" do
|
50
95
|
result = nil
|
51
96
|
KeywordSearch.search(NAME_AND_QUOTED_AGE) do |with|
|
@@ -53,9 +98,9 @@ context "KeywordSearch" do
|
|
53
98
|
result = Integer(values.first)
|
54
99
|
end
|
55
100
|
end
|
56
|
-
assert_equal 26, result
|
101
|
+
assert_equal 26, result
|
57
102
|
end
|
58
|
-
|
103
|
+
|
59
104
|
specify "quoted keyword term with whitespace" do
|
60
105
|
result = nil
|
61
106
|
KeywordSearch.search(DEFAULT_AGE_WITH_QUOTED_AGE) do |with|
|
@@ -64,20 +109,20 @@ context "KeywordSearch" do
|
|
64
109
|
result = values.first
|
65
110
|
end
|
66
111
|
end
|
67
|
-
assert_equal 'bruce williams', result
|
112
|
+
assert_equal 'bruce williams', result
|
68
113
|
end
|
69
|
-
|
114
|
+
|
70
115
|
specify "single quoted keyword term with whitespace" do
|
71
|
-
result = nil
|
116
|
+
result = nil
|
72
117
|
r = KeywordSearch.search(DEFAULT_AGE_WITH_SINGLE_QUOTED_AGE) do |with|
|
73
118
|
with.default_keyword :age
|
74
119
|
with.keyword :name do |values|
|
75
120
|
result = values.first
|
76
121
|
end
|
77
122
|
end
|
78
|
-
assert_equal 'bruce williams', result
|
123
|
+
assert_equal 'bruce williams', result
|
79
124
|
end
|
80
|
-
|
125
|
+
|
81
126
|
specify "nested single quote is accumulated" do
|
82
127
|
result = nil
|
83
128
|
KeywordSearch.search(NAME_WITH_NESTED_SINGLE_QUOTES) do |with|
|
@@ -86,9 +131,9 @@ context "KeywordSearch" do
|
|
86
131
|
result = values.first
|
87
132
|
end
|
88
133
|
end
|
89
|
-
assert_equal %<d'arcy d'uberville>, result
|
134
|
+
assert_equal %<d'arcy d'uberville>, result
|
90
135
|
end
|
91
|
-
|
136
|
+
|
92
137
|
specify "nested double quote is accumulated" do
|
93
138
|
result = nil
|
94
139
|
KeywordSearch.search(%<'he was called "jake"'>) do |with|
|
@@ -97,9 +142,9 @@ context "KeywordSearch" do
|
|
97
142
|
result = values.first
|
98
143
|
end
|
99
144
|
end
|
100
|
-
assert_equal %<he was called "jake">, result
|
145
|
+
assert_equal %<he was called "jake">, result
|
101
146
|
end
|
102
|
-
|
147
|
+
|
103
148
|
specify "bare single quote in unquoted literal is accumulated" do
|
104
149
|
result = nil
|
105
150
|
KeywordSearch.search(%<bruce's age:27>) do |with|
|
@@ -108,9 +153,9 @@ context "KeywordSearch" do
|
|
108
153
|
result = values.first
|
109
154
|
end
|
110
155
|
end
|
111
|
-
assert_equal %<bruce's>, result
|
156
|
+
assert_equal %<bruce's>, result
|
112
157
|
end
|
113
|
-
|
158
|
+
|
114
159
|
specify "single quoted literal is accumulated" do
|
115
160
|
result = nil
|
116
161
|
KeywordSearch.search(%<foo 'bruce williams' age:27>) do |with|
|
@@ -119,9 +164,9 @@ context "KeywordSearch" do
|
|
119
164
|
result = values.last
|
120
165
|
end
|
121
166
|
end
|
122
|
-
assert_equal %<bruce williams>, result
|
167
|
+
assert_equal %<bruce williams>, result
|
123
168
|
end
|
124
|
-
|
169
|
+
|
125
170
|
specify "period in literal is accumulated" do
|
126
171
|
result = nil
|
127
172
|
KeywordSearch.search(%<okay... age:27>) do |with|
|
@@ -130,9 +175,9 @@ context "KeywordSearch" do
|
|
130
175
|
result = values.first
|
131
176
|
end
|
132
177
|
end
|
133
|
-
assert_equal %<okay...>, result
|
178
|
+
assert_equal %<okay...>, result
|
134
179
|
end
|
135
|
-
|
180
|
+
|
136
181
|
specify "parse error results in exception" do
|
137
182
|
assert_raises(KeywordSearch::ParseError) do
|
138
183
|
KeywordSearch.search(%<we_do_not_allow:! or ::>) do |with|
|
@@ -143,7 +188,7 @@ context "KeywordSearch" do
|
|
143
188
|
end
|
144
189
|
end
|
145
190
|
end
|
146
|
-
|
191
|
+
|
147
192
|
specify "can use apostrophes in unquoted literal" do
|
148
193
|
result = nil
|
149
194
|
KeywordSearch.search(%<d'correct>) do |with|
|
@@ -154,7 +199,7 @@ context "KeywordSearch" do
|
|
154
199
|
end
|
155
200
|
assert_equal "d'correct", result
|
156
201
|
end
|
157
|
-
|
202
|
+
|
158
203
|
specify "can use apostrophes in unquoted literal values" do
|
159
204
|
result = nil
|
160
205
|
KeywordSearch.search(%<text:d'correct>) do |with|
|
@@ -165,7 +210,7 @@ context "KeywordSearch" do
|
|
165
210
|
end
|
166
211
|
assert_equal "d'correct", result
|
167
212
|
end
|
168
|
-
|
213
|
+
|
169
214
|
specify "cannot use an apostrophe at the beginning on an unquoted literal" do
|
170
215
|
assert_raises(KeywordSearch::ParseError) do
|
171
216
|
KeywordSearch.search(%<'thisiswrong>) do |with|
|
@@ -176,7 +221,7 @@ context "KeywordSearch" do
|
|
176
221
|
end
|
177
222
|
end
|
178
223
|
end
|
179
|
-
|
224
|
+
|
180
225
|
specify "keywords are case sensitive" do
|
181
226
|
result = nil
|
182
227
|
KeywordSearch.search(%<Text:justtesting>) do |with|
|
@@ -185,11 +230,11 @@ context "KeywordSearch" do
|
|
185
230
|
end
|
186
231
|
with.keyword :Text do |values|
|
187
232
|
result = :big
|
188
|
-
end
|
233
|
+
end
|
189
234
|
end
|
190
235
|
assert_equal :big, result
|
191
236
|
end
|
192
|
-
|
237
|
+
|
193
238
|
specify "values are case sensitive" do
|
194
239
|
result = nil
|
195
240
|
KeywordSearch.search(%<text:Big>) do |with|
|
@@ -199,11 +244,94 @@ context "KeywordSearch" do
|
|
199
244
|
end
|
200
245
|
assert_equal 'Big', result
|
201
246
|
end
|
202
|
-
|
203
|
-
end
|
204
247
|
|
248
|
+
specify "spaces are condensed" do
|
249
|
+
result = nil
|
250
|
+
KeywordSearch.search(%< this is some text >) do |with|
|
251
|
+
with.default_keyword :text
|
252
|
+
with.keyword :text do |values|
|
253
|
+
result = values
|
254
|
+
end
|
255
|
+
end
|
256
|
+
assert_equal %w(this is some text), result
|
257
|
+
end
|
258
|
+
|
259
|
+
specify "an empty search is successful" do
|
260
|
+
result = nil
|
261
|
+
KeywordSearch.search(%<>) do |with|
|
262
|
+
with.default_keyword :text
|
263
|
+
with.keyword :text do |values|
|
264
|
+
result = values
|
265
|
+
end
|
266
|
+
end
|
267
|
+
assert_nil result
|
268
|
+
end
|
269
|
+
|
270
|
+
specify 'a negative search' do
|
271
|
+
result = nil
|
272
|
+
|
273
|
+
KeywordSearch.search(%<-site:google.com>) do |with|
|
274
|
+
with.keyword :site do |values, positive|
|
275
|
+
result = [ values, positive ]
|
276
|
+
end
|
277
|
+
end
|
278
|
+
assert_equal [ [ 'google.com' ], false ], result
|
279
|
+
end
|
280
|
+
|
281
|
+
specify 'a positive search' do
|
282
|
+
result = nil
|
283
|
+
|
284
|
+
KeywordSearch.search(%<+site:google.com>) do |with|
|
285
|
+
with.keyword :site do |values, positive|
|
286
|
+
result = [ values, positive ]
|
287
|
+
end
|
288
|
+
end
|
289
|
+
assert_equal [ [ 'google.com' ], true ], result
|
290
|
+
end
|
291
|
+
|
292
|
+
specify 'a search with no sign' do
|
293
|
+
result = nil
|
294
|
+
|
295
|
+
KeywordSearch.search(%<site:google.com>) do |with|
|
296
|
+
with.keyword :site do |values, positive|
|
297
|
+
result = [ values, positive ]
|
298
|
+
end
|
299
|
+
end
|
300
|
+
assert_equal [ [ 'google.com' ], true ], result
|
301
|
+
end
|
302
|
+
|
303
|
+
specify 'a term should default to positive with no sign' do
|
304
|
+
result = nil
|
205
305
|
|
306
|
+
KeywordSearch.search(%<-site:google.com inurl:atom>) do |with|
|
307
|
+
with.keyword :inurl do |values, positive|
|
308
|
+
result = [ values, positive ]
|
309
|
+
end
|
310
|
+
end
|
311
|
+
assert_equal [ %w(atom), true ], result
|
312
|
+
end
|
206
313
|
|
314
|
+
specify 'a negative and positive search to the default keyword' do
|
315
|
+
result = []
|
207
316
|
|
317
|
+
KeywordSearch.search(%<text -google.com search>) do |with|
|
318
|
+
with.default_keyword :text
|
319
|
+
with.keyword :text do |values, positive|
|
320
|
+
result << [ values, positive ]
|
321
|
+
end
|
322
|
+
end
|
323
|
+
assert_equal [ [ %w(text search), true ], [ %w(google.com), false ] ], result
|
324
|
+
end
|
208
325
|
|
326
|
+
specify 'a negative search to the default keyword with quotes' do
|
327
|
+
result = []
|
209
328
|
|
329
|
+
KeywordSearch.search(%<-google.com>) do |with|
|
330
|
+
with.default_keyword :text
|
331
|
+
with.keyword :text do |values, positive|
|
332
|
+
result << [ values, positive ]
|
333
|
+
end
|
334
|
+
end
|
335
|
+
assert_equal [ [ %w(google.com), false ] ], result
|
336
|
+
end
|
337
|
+
end
|