wvanbergen-scoped_search 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -1,21 +1,18 @@
1
1
  = scoped_search
2
2
 
3
- This simple plugin will make it easy to search your ActiveRecord models. Searching is performed using a query string, which should be passed to the named_scope +search_for+ that uses SQL %LIKE% conditions for searching.
3
+ This simple plugin will make it easy to search your ActiveRecord models. Searching is performed using a query string, which should be passed to the named_scope <tt>search_for</tt> that uses SQL %LIKE% conditions for searching. You can specify what fields should be used for searching.
4
4
 
5
5
  == Installation
6
6
 
7
- You can enable scoped_search as a Ruby gem. First, you have to install the gem on your system.
8
-
9
- gem sources -a http://gems.github.com
10
- sudo gem install wvanbergen-scoped_search
11
-
12
- Now, you must enable the gem in your +environment.rb+ configuration:
7
+ You can enable <tt>scoped_search</tt> as a Ruby gem. You must enable the gem in your <tt>environment.rb</tt> configuration:
13
8
 
14
9
  Rails::Initializer.run do |config|
15
10
  ...
16
- config.gem 'wvanbergen-scoped_search', :lib => 'scoped_search'
11
+ config.gem 'wvanbergen-scoped_search', :lib => 'scoped_search', :source => 'http://gems.github.com/'
17
12
  end
18
13
 
14
+ Make sure the gem is installed by running <tt>rake gems:install</tt> in you project root. You can also install the gem by running <tt>sudo gem install wvanbergen-scoped_search -s http://gems.github.com</tt>
15
+
19
16
  You can use scoped_search as a Rails plugin as well, but this is deprecated. Simply download or
20
17
  clone scoped_search into your +vendor/plugins+ directory of your project.
21
18
 
data/lib/scoped_search.rb CHANGED
@@ -1,9 +1,11 @@
1
-
2
-
3
1
  module ScopedSearch
4
2
 
5
3
  module ClassMethods
6
4
 
5
+ def self.extended(base)
6
+ require 'scoped_search/query_language_parser'
7
+ end
8
+
7
9
  # Creates a named scope in the class it was called upon
8
10
  def searchable_on(*fields)
9
11
  self.cattr_accessor :scoped_search_fields
@@ -20,9 +22,8 @@ module ScopedSearch
20
22
  else
21
23
  conditions = []
22
24
  query_params = {}
23
- class << search_string; include ScopedSearch::QueryStringParser; end # TODO: fix me!
24
-
25
- search_string.to_search_query.each_with_index do |search_condition, index|
25
+
26
+ QueryLanguageParser.parse(search_string).each_with_index do |search_condition, index|
26
27
  keyword_name = "keyword_#{index}".to_sym
27
28
  query_params[keyword_name] = "%#{search_condition.first}%"
28
29
 
@@ -49,5 +50,4 @@ module ScopedSearch
49
50
  end
50
51
  end
51
52
 
52
- require 'scoped_search/query_string_parser'
53
53
  ActiveRecord::Base.send(:extend, ScopedSearch::ClassMethods)
@@ -0,0 +1,81 @@
1
+ module ScopedSearch
2
+
3
+ class QueryLanguageParser
4
+
5
+ def parse_query(query = nil)
6
+ return build_conditions_tree(tokenize(query))
7
+ end
8
+
9
+ def self.parse(query)
10
+ self.new.parse_query(query)
11
+ end
12
+
13
+ protected
14
+
15
+ def build_conditions_tree(tokens)
16
+ conditions_tree = []
17
+
18
+ negate = false
19
+ tokens.each do |item|
20
+ case item
21
+ when :not
22
+ negate = true
23
+ else
24
+ conditions_tree << (negate ? [item, :not] : [item, :like])
25
+ negate = false
26
+ end
27
+ end
28
+ return conditions_tree
29
+ end
30
+
31
+ def tokenize(query)
32
+ tokens = []
33
+ current_token = ""
34
+ quoted_string_openend = false
35
+
36
+ query.each_char do |char|
37
+
38
+ case char
39
+ when /\s/
40
+ if quoted_string_openend
41
+ current_token << char
42
+ elsif current_token.length > 0
43
+ tokens << current_token
44
+ current_token = ""
45
+ end
46
+
47
+ when '-'
48
+ if quoted_string_openend || current_token.length > 0
49
+ current_token << char
50
+ else
51
+ tokens << :not
52
+ end
53
+
54
+ when '"'
55
+ if quoted_string_openend
56
+ if current_token.length > 0
57
+ if current_token[-1,1] == "\\"
58
+ current_token[-1] = char
59
+ else
60
+ tokens << current_token
61
+ current_token = ""
62
+ quoted_string_openend = false
63
+ end
64
+ else
65
+ quoted_string_openend = false
66
+ end
67
+
68
+ else
69
+ quoted_string_openend = true
70
+ end
71
+
72
+ else
73
+ current_token << char
74
+ end
75
+
76
+ end
77
+ tokens << current_token if current_token.length > 0
78
+ return tokens
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,84 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class QueryLanguageTest < Test::Unit::TestCase
4
+
5
+ # change this function if you switch to another query language parser
6
+ def parse_query(query)
7
+ ScopedSearch::QueryLanguageParser.parse(query)
8
+ end
9
+
10
+ def test_empty_search_query
11
+ parsed = parse_query('')
12
+ assert_equal 0, parsed.length
13
+
14
+ parsed = parse_query("\t \n")
15
+ assert_equal 0, parsed.length
16
+ end
17
+
18
+ def test_single_keyword
19
+ parsed = parse_query('hallo')
20
+ assert_equal 1, parsed.length
21
+ assert_equal 'hallo', parsed.first.first
22
+
23
+ parsed = parse_query(' hallo ')
24
+ assert_equal 1, parsed.length
25
+ assert_equal 'hallo', parsed.first.first
26
+ end
27
+
28
+ def test_multiple_keywords
29
+ parsed = parse_query(' hallo willem')
30
+ assert_equal 2, parsed.length
31
+ assert_equal 'willem', parsed.last.first
32
+
33
+ parsed = parse_query(" hallo willem van\tbergen ")
34
+ assert_equal 4, parsed.length
35
+ assert_equal 'hallo', parsed[0].first
36
+ assert_equal 'willem', parsed[1].first
37
+ assert_equal 'van', parsed[2].first
38
+ assert_equal 'bergen', parsed[3].first
39
+ end
40
+
41
+ def test_quoted_keywords
42
+ parsed = parse_query(' "hallo"')
43
+ assert_equal 1, parsed.length
44
+ assert_equal 'hallo', parsed.first.first
45
+
46
+ parsed = parse_query(' "hallo willem"')
47
+ assert_equal 1, parsed.length
48
+ assert_equal 'hallo willem', parsed.first.first
49
+
50
+ parsed = parse_query(' "hallo willem')
51
+ assert_equal 1, parsed.length
52
+ assert_equal 'hallo willem', parsed.first.first
53
+
54
+ parsed = parse_query(' "hallo wi"llem"')
55
+ assert_equal 2, parsed.length
56
+ assert_equal 'hallo wi', parsed.first.first
57
+ assert_equal 'llem', parsed.last.first
58
+ end
59
+
60
+ def test_quote_escaping
61
+ parsed = parse_query(' "hallo wi\\"llem"')
62
+ assert_equal 1, parsed.length
63
+ assert_equal 'hallo wi"llem', parsed.first.first
64
+
65
+ parsed = parse_query('"\\"hallo willem\\""')
66
+ assert_equal 1, parsed.length
67
+ assert_equal '"hallo willem"', parsed.first.first
68
+ end
69
+
70
+ def test_negation
71
+ parsed = parse_query('-willem')
72
+ assert_equal 1, parsed.length
73
+ assert_equal 'willem', parsed.first.first
74
+ assert_equal :not, parsed.first.last
75
+
76
+ parsed = parse_query('123 -"456 789"')
77
+ assert_equal 2, parsed.length
78
+ assert_equal '123', parsed.first.first
79
+ assert_equal :like, parsed.first.last
80
+
81
+ assert_equal '456 789', parsed.last.first
82
+ assert_equal :not, parsed.last.last
83
+ end
84
+ end
File without changes
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wvanbergen-scoped_search
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Willem van Bergen
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-09-04 00:00:00 -07:00
12
+ date: 2008-09-06 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -30,10 +30,10 @@ files:
30
30
  - lib
31
31
  - lib/scoped_search
32
32
  - lib/scoped_search.rb
33
- - lib/scoped_search/query_string_parser.rb
33
+ - lib/scoped_search/query_language_parser.rb
34
34
  - test
35
- - test/query_building_test.rb
36
- - test/query_string_parser_test.rb
35
+ - test/query_language_test.rb
36
+ - test/search_for_test.rb
37
37
  - test/tasks.rake
38
38
  - test/test_helper.rb
39
39
  has_rdoc: false
@@ -63,5 +63,5 @@ signing_key:
63
63
  specification_version: 2
64
64
  summary: A Rails plugin to search your models using a named_scope
65
65
  test_files:
66
- - test/query_building_test.rb
67
- - test/query_string_parser_test.rb
66
+ - test/query_language_test.rb
67
+ - test/search_for_test.rb
@@ -1,70 +0,0 @@
1
- module ScopedSearch::QueryStringParser
2
-
3
- def to_search_query
4
- items = lex_for_query_string_parsing
5
- search_conditions = []
6
-
7
- negate = false
8
- items.each do |item|
9
- case item
10
- when :not
11
- negate = true
12
- else
13
- search_conditions << (negate ? [item, :not] : [item])
14
- negate = false
15
- end
16
- end
17
- return search_conditions
18
- end
19
-
20
- def lex_for_query_string_parsing
21
- terms = []
22
- current_term = ""
23
- quoted_string_openend = false
24
-
25
- self.each_char do |char|
26
-
27
- case char
28
- when /\s/
29
- if quoted_string_openend
30
- current_term << char
31
- elsif current_term.length > 0
32
- terms << current_term
33
- current_term = ""
34
- end
35
-
36
- when '-'
37
- if quoted_string_openend || current_term.length > 0
38
- current_term << char
39
- else
40
- terms << :not
41
- end
42
-
43
- when '"'
44
- if quoted_string_openend
45
- if current_term.length > 0
46
- if current_term[-1,1] == "\\"
47
- current_term[-1] = char
48
- else
49
- terms << current_term
50
- current_term = ""
51
- quoted_string_openend = false
52
- end
53
- else
54
- quoted_string_openend = false
55
- end
56
-
57
- else
58
- quoted_string_openend = true
59
- end
60
-
61
- else
62
- current_term << char
63
- end
64
-
65
- end
66
- terms << current_term if current_term.length > 0
67
- return terms
68
- end
69
-
70
- end
@@ -1,74 +0,0 @@
1
- require File.dirname(__FILE__) + '/test_helper'
2
-
3
- class String
4
- include ScopedSearch::QueryStringParser
5
- end
6
-
7
- class QueryStringParserTest < Test::Unit::TestCase
8
-
9
- def test_empty_search_query
10
- parsed = ''.lex_for_query_string_parsing
11
- assert_equal 0, parsed.length
12
-
13
- parsed = "\t \n".lex_for_query_string_parsing
14
- assert_equal 0, parsed.length
15
- end
16
-
17
- def test_single_keyword
18
-
19
- parsed = 'hallo'.lex_for_query_string_parsing
20
- assert_equal 1, parsed.length
21
- assert_equal 'hallo', parsed.first
22
-
23
- parsed = ' hallo '.lex_for_query_string_parsing
24
- assert_equal 1, parsed.length
25
- assert_equal 'hallo', parsed.first
26
- end
27
-
28
- def test_multiple_keywords
29
- parsed = ' hallo willem'.lex_for_query_string_parsing
30
- assert_equal 2, parsed.length
31
- assert_equal 'willem', parsed.last
32
- end
33
-
34
- def test_quoted_keywords
35
- parsed = ' "hallo"'.lex_for_query_string_parsing
36
- assert_equal 1, parsed.length
37
- assert_equal 'hallo', parsed.first
38
-
39
- parsed = ' "hallo willem"'.lex_for_query_string_parsing
40
- assert_equal 1, parsed.length
41
- assert_equal 'hallo willem', parsed.first
42
-
43
- parsed = ' "hallo willem'.lex_for_query_string_parsing
44
- assert_equal 1, parsed.length
45
- assert_equal 'hallo willem', parsed.first
46
-
47
- parsed = ' "hallo wi"llem"'.lex_for_query_string_parsing
48
- assert_equal 2, parsed.length
49
- assert_equal 'hallo wi', parsed.first
50
- assert_equal 'llem', parsed.last
51
- end
52
-
53
- def test_quote_escaping
54
- parsed = ' "hallo wi\\"llem"'.lex_for_query_string_parsing
55
- assert_equal 1, parsed.length
56
- assert_equal 'hallo wi"llem', parsed.first
57
-
58
- parsed = '"\\"hallo willem\\""'.lex_for_query_string_parsing
59
- assert_equal 1, parsed.length
60
- assert_equal '"hallo willem"', parsed.first
61
- end
62
-
63
- def test_negation
64
- parsed = '-willem'.lex_for_query_string_parsing
65
- assert_equal 2, parsed.length
66
- assert_equal :not, parsed.first
67
-
68
- parsed = '123 -"456 789"'.lex_for_query_string_parsing
69
- assert_equal 3, parsed.length
70
- assert_equal '123', parsed[0]
71
- assert_equal :not, parsed[1]
72
- assert_equal '456 789', parsed[2]
73
- end
74
- end