textquery 0.1.7 → 0.1.8

Sign up to get free protection for your applications and to get access to all the features.
data/.rspec ADDED
File without changes
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
@@ -0,0 +1,44 @@
1
+ # TextQuery
2
+
3
+ Does it match? When regular expressions are not enough, textquery is the answer. For
4
+ example, regular expressions cannot evaluate recursive rules and often result in
5
+ overly verbose and complicated expressions.
6
+
7
+ Textquery is a simple PEG grammar with support for:
8
+
9
+ - AND (spaces are implicit AND's)
10
+ - OR
11
+ - NOT (- is an alias)
12
+ - 'quoted strings'
13
+ - fuzzy matching
14
+ - case (in)sensitive
15
+ - custom delimeters
16
+
17
+ ## Example
18
+
19
+ ```ruby
20
+ TextQuery.new("'to be' OR NOT 'to_be'").match?("to be") # => true
21
+
22
+ TextQuery.new("-test").match?("some string of text") # => true
23
+ TextQuery.new("NOT test").match?("some string of text") # => true
24
+
25
+ TextQuery.new("a AND b").match?("b a") # => true
26
+ TextQuery.new("a AND b").match?("a c") # => false
27
+
28
+ q = TextQuery.new("a AND (b AND NOT (c OR d))")
29
+ q.match?("d a b") # => false
30
+ q.match?("b") # => false
31
+ q.match?("a b cdefg") # => true
32
+
33
+ TextQuery.new("a~").match?("adf") # => true
34
+ TextQuery.new("~a").match?("dfa") # => true
35
+ TextQuery.new("~a~").match?("daf") # => true
36
+ TextQuery.new("2~a~1").match?("edaf") # => true
37
+ TextQuery.new("2~a~2").match?("edaf") # => false
38
+
39
+ TextQuery.new("a", :ignorecase => true).match?("A b cD") # => true
40
+ ```
41
+
42
+ ## License
43
+
44
+ The MIT License - Copyright (c) 2011 Ilya Grigorik
data/Rakefile CHANGED
@@ -1,19 +1,10 @@
1
- require 'rake'
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
2
3
 
3
- begin
4
- require 'jeweler'
5
- Jeweler::Tasks.new do |gemspec|
6
- gemspec.name = "textquery"
7
- gemspec.summary = "Evaluate any text against a collection of match rules"
8
- gemspec.description = gemspec.summary
9
- gemspec.email = "ilya@igvita.com"
10
- gemspec.homepage = "http://github.com/igrigorik/textquery"
11
- gemspec.authors = ["Ilya Grigorik"]
12
- gemspec.add_dependency("treetop")
13
- gemspec.rubyforge_project = "textquery"
14
- end
4
+ require 'rspec/core/rake_task'
15
5
 
16
- Jeweler::GemcutterTasks.new
17
- rescue LoadError
18
- puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
19
- end
6
+ desc "Run all RSpec tests"
7
+ RSpec::Core::RakeTask.new(:spec)
8
+
9
+ task :default => :spec
10
+ task :test => [:spec]
@@ -17,7 +17,7 @@ else
17
17
  RegExp::IGNORECACASE = Regexp::IGNORECASE
18
18
  end
19
19
 
20
- FUZZY = RegExp.new('(\d)*(~)?([^~]+)(~)?(\d)*$')
20
+ FUZZY = RegExp.new('(?:(\d)*(~))?([^~]+)(?:(~)?(\d)*)$')
21
21
 
22
22
  class WordMatch < Treetop::Runtime::SyntaxNode
23
23
 
@@ -52,6 +52,9 @@ class WordMatch < Treetop::Runtime::SyntaxNode
52
52
  end
53
53
  end
54
54
 
55
+ def accept(&block)
56
+ block.call(:value, text_value)
57
+ end
55
58
  end
56
59
 
57
60
  Treetop.load File.dirname(__FILE__) + "/textquery_grammar"
@@ -88,6 +91,16 @@ class TextQuery
88
91
  end
89
92
  alias :match? :eval
90
93
 
94
+ def accept(options = {}, &block)
95
+ update_options(options) if not options.empty?
96
+
97
+ if @query
98
+ @query.accept(&block)
99
+ else
100
+ raise TextQueryError, 'no query specified'
101
+ end
102
+ end
103
+
91
104
  def terminal_failures
92
105
  @parser.terminal_failures
93
106
  end
@@ -96,6 +109,6 @@ class TextQuery
96
109
 
97
110
  def update_options(options)
98
111
  @options = {:delim => ' '}.merge(options)
99
- @options[:delim] = "(#{[@options[:delim]].flatten.map { |opt| RegExp.escape(opt) }.join("|")})"
112
+ @options[:delim] = "(#{[@options[:delim]].flatten.map { |opt| opt.is_a?(Regexp) ? opt : RegExp.escape(opt) }.join("|")})"
100
113
  end
101
114
  end
@@ -9,12 +9,20 @@ grammar TextQueryGrammar
9
9
  def eval(text, opt)
10
10
  operator.eval(op1.eval(text, opt), op2.eval(text, opt))
11
11
  end
12
+
13
+ def accept(&block)
14
+ operator.accept(op1.accept(&block), op2.accept(&block), &block)
15
+ end
12
16
  }
13
17
  /
14
18
  op1:value [\s]+ op2:expression {
15
19
  def eval(text, opt)
16
20
  op1.eval(text, opt) && op2.eval(text, opt)
17
21
  end
22
+
23
+ def accept(&block)
24
+ block.call(:and, op1.accept(&block), op2.accept(&block))
25
+ end
18
26
  }
19
27
  end
20
28
 
@@ -23,12 +31,20 @@ grammar TextQueryGrammar
23
31
  def eval(a,b)
24
32
  a && b
25
33
  end
34
+
35
+ def accept(a, b, &block)
36
+ block.call(:and, a, b)
37
+ end
26
38
  }
27
39
  /
28
40
  'OR' {
29
41
  def eval(a,b)
30
42
  a || b
31
43
  end
44
+
45
+ def accept(a, b, &block)
46
+ block.call(:or, a, b)
47
+ end
32
48
  }
33
49
  end
34
50
 
@@ -37,6 +53,10 @@ grammar TextQueryGrammar
37
53
  def eval(a)
38
54
  not a
39
55
  end
56
+
57
+ def accept(a, &block)
58
+ block.call(:not, a)
59
+ end
40
60
  }
41
61
  end
42
62
 
@@ -69,24 +89,40 @@ grammar TextQueryGrammar
69
89
  def eval(text, opt)
70
90
  expression.eval(text, opt)
71
91
  end
92
+
93
+ def accept(&block)
94
+ expression.accept(&block)
95
+ end
72
96
  }
73
97
  /
74
98
  operator:unary space value {
75
99
  def eval(text, opt)
76
100
  operator.eval(value.eval(text, opt))
77
101
  end
102
+
103
+ def accept(&block)
104
+ operator.accept(value.accept(&block), &block)
105
+ end
78
106
  }
79
107
  /
80
108
  double_quote space double_quote_words space double_quote {
81
109
  def eval(text, opt)
82
110
  double_quote_words.eval(text, opt)
83
111
  end
112
+
113
+ def accept(&block)
114
+ double_quote_words.accept(&block)
115
+ end
84
116
  }
85
117
  /
86
118
  single_quote space single_quote_words space single_quote {
87
119
  def eval(text, opt)
88
120
  single_quote_words.eval(text, opt)
89
121
  end
122
+
123
+ def accept(&block)
124
+ single_quote_words.accept(&block)
125
+ end
90
126
  }
91
127
  /
92
128
  word
@@ -1,10 +1,9 @@
1
1
  # encoding: UTF-8
2
2
 
3
- require "rubygems"
4
- require "spec"
3
+ require "rspec"
5
4
  require "pp"
6
5
 
7
- require "lib/textquery"
6
+ require "textquery"
8
7
 
9
8
  # Resources:
10
9
  # - http://github.com/nathansobo/treetop
@@ -137,6 +136,13 @@ describe TextQuery do
137
136
  parse('"to be" OR NOT "to be"').eval("to be").should be_true
138
137
  end
139
138
 
139
+ it "should accept unbalanced quotes" do
140
+ parse("awesome").eval("M&M's are awesome").should be_true
141
+ parse("M&M's").eval("M&M's are awesome").should be_true
142
+ parse("M&M's AND awesome").eval("M&M's are awesome").should be_true
143
+ parse("M&M's AND fail").eval("M&M's are awesome").should be_false
144
+ end
145
+
140
146
  it "should accept mixed quotes inside the exact match queries" do
141
147
  parse("seattle's best").eval("seattle's best").should be_true
142
148
 
@@ -204,6 +210,11 @@ describe TextQuery do
204
210
  q.parse("~更は出~ OR ~尽く~").eval(JP).should be_true
205
211
  end
206
212
 
213
+ it "should work with queries starting with numbers" do
214
+ q = TextQuery.new('3827')
215
+ q.parse('abc 123 3827 9382').should be_true
216
+ end
217
+
207
218
  it "should be case insensitive" do
208
219
  TextQuery.new("a", :ignorecase => true).match?("A b cD").should be_true
209
220
  TextQuery.new("a AND CD", :ignorecase => true).match?("A b cD").should be_true
@@ -236,5 +247,28 @@ describe TextQuery do
236
247
  it 'should not match just the delimiter' do
237
248
  TextQuery.new("a*b", :delim => ["*", "<"]).match?("over<under").should be_false
238
249
  end
250
+
251
+ it 'should accept a Regexp as a delimiter' do
252
+ TextQuery.new("a", :delim => [%r{\b}]).match?("a.b").should be_true
253
+ TextQuery.new("a b", :delim => [%r{\b}]).match?("a.b").should be_true
254
+ TextQuery.new("a b", :delim => [%r{\b}]).match?("a.c").should be_false
255
+ end
256
+
257
+ it 'should OR multiple Regexp delimiters and match on all words' do
258
+ TextQuery.new("cd", :delim => [%r{\d}, %r{\.\.}]).match?("ab2cd..ef").should be_true
259
+ TextQuery.new("ef", :delim => [%r{\d}, %r{\.\.}]).match?("ab2cd..ef").should be_true
260
+ TextQuery.new("ab2cd", :delim => [%r{\d}, %r{\.\.}]).match?("ab2cd..ef").should be_true
261
+ end
262
+
263
+ it 'should accept mixed Strings and Regexps as delimiters' do
264
+ TextQuery.new("a", :delim => [%r{a{2,3}}]).match?("aab").should be_false
265
+ TextQuery.new("a", :delim => [%r{a{2,3}}, 'b']).match?("aab").should be_false
266
+ TextQuery.new("b", :delim => [%r{a{2,3}}, 'a']).match?("aab").should be_true
267
+ end
268
+
269
+ it 'should allow query to be traversed' do
270
+ TextQuery.new("a b").accept { |*a| a }.should == [ :and, [ :value, 'a' ], [ :value, 'b' ] ]
271
+ TextQuery.new("a OR b").accept { |*a| a }.should == [ :or, [ :value, 'a' ], [ :value, 'b' ] ]
272
+ end
239
273
  end
240
274
  end
@@ -1,56 +1,24 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
1
  # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
5
3
 
6
4
  Gem::Specification.new do |s|
7
- s.name = %q{textquery}
8
- s.version = "0.1.7"
5
+ s.name = "textquery"
6
+ s.version = "0.1.8"
9
7
 
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Ilya Grigorik"]
12
- s.date = %q{2010-09-04}
13
- s.description = %q{Evaluate any text against a collection of match rules}
14
- s.email = %q{ilya@igvita.com}
15
- s.extra_rdoc_files = [
16
- "README.rdoc"
17
- ]
18
- s.files = [
19
- ".gitignore",
20
- "README.rdoc",
21
- "Rakefile",
22
- "VERSION",
23
- "benchmark/benchmark.rb",
24
- "benchmark/sample.txt",
25
- "examples/web.rb",
26
- "lib/textquery.rb",
27
- "lib/textquery/textquery.rb",
28
- "lib/textquery/textquery_grammar.treetop",
29
- "spec/textquery_spec.rb",
30
- "textquery.gemspec"
31
- ]
32
- s.homepage = %q{http://github.com/igrigorik/textquery}
33
- s.rdoc_options = ["--charset=UTF-8"]
34
- s.require_paths = ["lib"]
35
- s.rubyforge_project = %q{textquery}
36
- s.rubygems_version = %q{1.3.7}
37
- s.summary = %q{Evaluate any text against a collection of match rules}
38
- s.test_files = [
39
- "spec/textquery_spec.rb",
40
- "examples/web.rb"
41
- ]
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Ilya Grigorik"]
10
+ s.email = ["ilya@igvita.com"]
11
+ s.homepage = "http://github.com/igrigorik/textquery"
12
+ s.summary = "Evaluate any text against a collection of match rules"
13
+ s.description = s.summary
14
+ s.rubyforge_project = "textquery"
42
15
 
43
- if s.respond_to? :specification_version then
44
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
45
- s.specification_version = 3
16
+ s.add_dependency "treetop"
17
+ s.add_development_dependency "rspec"
18
+ s.add_development_dependency "rake"
46
19
 
47
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
48
- s.add_runtime_dependency(%q<treetop>, [">= 0"])
49
- else
50
- s.add_dependency(%q<treetop>, [">= 0"])
51
- end
52
- else
53
- s.add_dependency(%q<treetop>, [">= 0"])
54
- end
20
+ s.files = `git ls-files`.split("\n")
21
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
22
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
23
+ s.require_paths = ["lib"]
55
24
  end
56
-
metadata CHANGED
@@ -1,48 +1,61 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: textquery
3
- version: !ruby/object:Gem::Version
4
- prerelease: false
5
- segments:
6
- - 0
7
- - 1
8
- - 7
9
- version: 0.1.7
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.8
5
+ prerelease:
10
6
  platform: ruby
11
- authors:
7
+ authors:
12
8
  - Ilya Grigorik
13
9
  autorequire:
14
10
  bindir: bin
15
11
  cert_chain: []
16
-
17
- date: 2010-09-04 00:00:00 -04:00
18
- default_executable:
19
- dependencies:
20
- - !ruby/object:Gem::Dependency
12
+ date: 2011-06-28 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
21
15
  name: treetop
22
- prerelease: false
23
- requirement: &id001 !ruby/object:Gem::Requirement
16
+ requirement: &2152520940 !ruby/object:Gem::Requirement
24
17
  none: false
25
- requirements:
26
- - - ">="
27
- - !ruby/object:Gem::Version
28
- segments:
29
- - 0
30
- version: "0"
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
31
22
  type: :runtime
32
- version_requirements: *id001
23
+ prerelease: false
24
+ version_requirements: *2152520940
25
+ - !ruby/object:Gem::Dependency
26
+ name: rspec
27
+ requirement: &2152520500 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *2152520500
36
+ - !ruby/object:Gem::Dependency
37
+ name: rake
38
+ requirement: &2152520080 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *2152520080
33
47
  description: Evaluate any text against a collection of match rules
34
- email: ilya@igvita.com
48
+ email:
49
+ - ilya@igvita.com
35
50
  executables: []
36
-
37
51
  extensions: []
38
-
39
- extra_rdoc_files:
40
- - README.rdoc
41
- files:
52
+ extra_rdoc_files: []
53
+ files:
42
54
  - .gitignore
43
- - README.rdoc
55
+ - .rspec
56
+ - Gemfile
57
+ - README.md
44
58
  - Rakefile
45
- - VERSION
46
59
  - benchmark/benchmark.rb
47
60
  - benchmark/sample.txt
48
61
  - examples/web.rb
@@ -51,38 +64,29 @@ files:
51
64
  - lib/textquery/textquery_grammar.treetop
52
65
  - spec/textquery_spec.rb
53
66
  - textquery.gemspec
54
- has_rdoc: true
55
67
  homepage: http://github.com/igrigorik/textquery
56
68
  licenses: []
57
-
58
69
  post_install_message:
59
- rdoc_options:
60
- - --charset=UTF-8
61
- require_paths:
70
+ rdoc_options: []
71
+ require_paths:
62
72
  - lib
63
- required_ruby_version: !ruby/object:Gem::Requirement
73
+ required_ruby_version: !ruby/object:Gem::Requirement
64
74
  none: false
65
- requirements:
66
- - - ">="
67
- - !ruby/object:Gem::Version
68
- segments:
69
- - 0
70
- version: "0"
71
- required_rubygems_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ! '>='
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
80
  none: false
73
- requirements:
74
- - - ">="
75
- - !ruby/object:Gem::Version
76
- segments:
77
- - 0
78
- version: "0"
81
+ requirements:
82
+ - - ! '>='
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
79
85
  requirements: []
80
-
81
86
  rubyforge_project: textquery
82
- rubygems_version: 1.3.7
87
+ rubygems_version: 1.8.5
83
88
  signing_key:
84
89
  specification_version: 3
85
90
  summary: Evaluate any text against a collection of match rules
86
- test_files:
91
+ test_files:
87
92
  - spec/textquery_spec.rb
88
- - examples/web.rb
@@ -1,62 +0,0 @@
1
- = TextQuery
2
-
3
- Does it match? When regular expressions are not enough, textquery is the answer. For
4
- example, regular expressions cannot evaluate recursive rules and often result in
5
- overly verbose and complicated expressions.
6
-
7
- Textquery is a simple PEG grammar with support for:
8
- - AND (spaces are implicit AND's)
9
- - OR
10
- - NOT (- is an alias)
11
- - 'quoted strings'
12
- - fuzzy matching
13
- - case (in)sensitive
14
- - custom delimeters
15
-
16
- == Example
17
-
18
- TextQuery.new("'to be' OR NOT 'to_be'").match?("to be") # => true
19
-
20
- TextQuery.new("-test").match?("some string of text") # => true
21
- TextQuery.new("NOT test").match?("some string of text") # => true
22
-
23
- TextQuery.new("a AND b").match?("b a") # => true
24
- TextQuery.new("a AND b").match?("a c") # => false
25
-
26
- q = TextQuery.new("a AND (b AND NOT (c OR d))")
27
- q.match?("d a b") # => false
28
- q.match?("b") # => false
29
- q.match?("a b cdefg") # => true
30
-
31
- TextQuery.new("a~").match?("adf") # => true
32
- TextQuery.new("~a").match?("dfa") # => true
33
- TextQuery.new("~a~").match?("daf") # => true
34
- TextQuery.new("2~a~1").match?("edaf") # => true
35
- TextQuery.new("2~a~2").match?("edaf") # => false
36
-
37
- TextQuery.new("a", :ignorecase => true).match?("A b cD") # => true
38
-
39
- == License
40
-
41
- (The MIT License)
42
-
43
- Copyright (c) 2009 Ilya Grigorik
44
-
45
- Permission is hereby granted, free of charge, to any person obtaining
46
- a copy of this software and associated documentation files (the
47
- 'Software'), to deal in the Software without restriction, including
48
- without limitation the rights to use, copy, modify, merge, publish,
49
- distribute, sublicense, and/or sell copies of the Software, and to
50
- permit persons to whom the Software is furnished to do so, subject to
51
- the following conditions:
52
-
53
- The above copyright notice and this permission notice shall be
54
- included in all copies or substantial portions of the Software.
55
-
56
- THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
57
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
58
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
59
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
60
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
61
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
62
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/VERSION DELETED
@@ -1 +0,0 @@
1
- 0.1.7