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.
@@ -15,7 +15,7 @@ module KeywordSearch
15
15
  }
16
16
 
17
17
  action key {
18
- key = data[tokstart...p-1]
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
- value = data[tokstart..p-1]
28
- value = value[1..-2] if ["'", '"'].include?(value[0,1])
29
- (results[key || :default] ||= []) << value
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
- value = ( dquoted | squoted | bareword );
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
- pair = (bareword > start ':') % key value > start % value ;
64
+ match_mode = ('-' % negative_match | '+'? % positive_match ) ;
65
+
66
+ definition = match_mode? <: ( pair | value_only ) ;
43
67
 
44
- definition = ( pair | value > start > default % value) ' ';
45
- main := definition** 0
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.call(value)
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(values)
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
@@ -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