wvanbergen-scoped_search 1.0.1 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +41 -28
- data/Rakefile +1 -39
- data/lib/scoped_search/query_conditions_builder.rb +21 -10
- data/lib/scoped_search/query_language_parser.rb +1 -1
- data/tasks/database_tests.rake +20 -0
- data/tasks/documentation.rake +33 -0
- data/test/integration/api_test.rb +2 -1
- data/test/lib/test_models.rb +21 -21
- data/test/lib/test_schema.rb +2 -0
- data/test/unit/query_conditions_builder_test.rb +21 -1
- data/test/unit/query_language_test.rb +7 -2
- data/test/unit/search_for_test.rb +4 -2
- metadata +4 -6
- data/CHANGELOG.rdoc +0 -13
- data/TODO.rdoc +0 -6
data/README.rdoc
CHANGED
@@ -1,65 +1,78 @@
|
|
1
1
|
= scoped_search
|
2
2
|
|
3
|
-
|
3
|
+
The <b>scoped_search</b> Rails plugin makes it easy to search your ActiveRecord models. Searching is
|
4
|
+
performed using a query string, which should be passed to the named_scope *search_for* that uses SQL
|
5
|
+
<tt>LIKE %keyword%</tt> conditions for searching (ILIKE for Postgres). You can specify what fields
|
6
|
+
should be used for searching.
|
4
7
|
|
5
8
|
== Installing
|
6
9
|
|
7
|
-
|
10
|
+
The recommended method to enable scoped_search in your project is adding the scoped_search gem to your environment. Add the following code to your Rails configuration in <tt>config/environment.rb</tt>:
|
11
|
+
|
12
|
+
Rails::Initializer.run do |config|
|
13
|
+
...
|
14
|
+
config.gem 'wvanbergen-scoped_search', :lib => 'scoped_search',
|
15
|
+
source => 'http://gems.github.com/'
|
16
|
+
end
|
17
|
+
|
18
|
+
Run <tt>sudo rake gems:install</tt> to install the gem.
|
19
|
+
|
20
|
+
Another alternative is to install scoped_search as a Rails plugin:
|
21
|
+
|
22
|
+
script/plugin install git://github.com/wvanbergen/scoped_search.git
|
8
23
|
|
9
24
|
== Usage
|
10
25
|
|
11
26
|
First, you have to specify in what columns should be searched:
|
12
27
|
|
13
|
-
|
14
|
-
|
15
|
-
|
28
|
+
class User < ActiveRecord::Base
|
29
|
+
searchable_on :first_name, :last_name
|
30
|
+
end
|
16
31
|
|
17
32
|
|
18
33
|
Now, the <b>search_for</b> scope is available for queries. You should pass a query string to the scope.
|
19
34
|
This can be empty or nil, in which case all no search conditions are set (and all records will be returned).
|
20
35
|
|
21
|
-
|
36
|
+
User.search_for(params[:q]).each { |project| ... }
|
22
37
|
|
23
38
|
|
24
39
|
You can also search on associate models. This works with <b>belongs_to</b>, <b>has_one</b>, <b>has_many</b>,
|
25
40
|
<b>has_many :through</b>, and <b>HABTM</b>. For example if a User <b>has_many</b> Notes (title, content, created_at, updated_at)
|
26
41
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
42
|
+
class User < ActiveRecord::Base
|
43
|
+
has_many: notes
|
44
|
+
searchable_on :first_name, :last_name, :notes_title, :notes_content
|
45
|
+
end
|
32
46
|
|
33
47
|
The search query language is simple. It supports these constructs:
|
34
|
-
* <b>words:</b> some search keywords
|
35
|
-
* <b>phrases:</b> "a single search phrase"
|
36
|
-
* <b>negation:</b> "look for this" -"but do not look for this phrase and this" -word
|
37
|
-
* <b>OR words/phrases:</b> word/phrase OR word/phrase. Example: "Hello World" OR "Hello Moon"
|
48
|
+
* <b>words:</b> <tt>some search keywords</tt>
|
49
|
+
* <b>phrases:</b> <tt>"a single search phrase"</tt>
|
50
|
+
* <b>negation:</b> <tt>"look for this" -"but do not look for this phrase and this" -word</tt>
|
51
|
+
* <b>OR words/phrases:</b> word/phrase OR word/phrase. Example: <tt>"Hello World" OR "Hello Moon"</tt>
|
38
52
|
* <b>dates:</b> mm/dd/yyyy, dd/mm/yyyy, yyyy/mm/dd, yyyy-mm-dd
|
39
|
-
* <b>date ranges:</b> > date, >= date, < date, <= date, date TO date. Examples:
|
53
|
+
* <b>date ranges:</b> > date, >= date, < date, <= date, date TO date. Examples: <tt>> 30/05/1983</tt>, <tt>< 2009-01-30</tt>
|
40
54
|
|
41
|
-
This functionality is build on named_scope
|
55
|
+
This functionality is build on <tt>named_scope</tt>. The searchable_on statement creates
|
42
56
|
a named_scope *search_for*. Because of this, you can actually chain the call with
|
43
57
|
other scopes. For example, this can be very useful if you only want to search in
|
44
58
|
projects that are accessible by a given user.
|
45
59
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
# using chained named_scopes and will_paginate
|
52
|
-
Project.accessible_by(current_user).search_for(params[:q]).paginate(:page => params[:page], :include => :tasks)
|
60
|
+
class Project < ActiveRecord::Base
|
61
|
+
searchable_on :name, :description
|
62
|
+
named_scope :accessible_by, lambda { |user| ... }
|
63
|
+
end
|
53
64
|
|
65
|
+
# using chained named_scopes and will_paginate
|
66
|
+
Project.accessible_by(current_user).search_for(params[:q]).paginate(:page => params[:page], :include => :tasks)
|
54
67
|
|
55
68
|
== Additional resources
|
56
69
|
|
57
|
-
* Source code: http://github.com/wvanbergen/scoped_search
|
70
|
+
* Source code: http://github.com/wvanbergen/scoped_search/tree/master
|
58
71
|
* Project wiki: http://wiki.github.com/wvanbergen/scoped_search
|
59
72
|
* RDoc documentation: http://wvanbergen.github.com/scoped_search
|
60
|
-
* wvanbergen's blog posts: http://techblog.floorplanner.com/tag/scoped_search
|
73
|
+
* wvanbergen's blog posts: http://techblog.floorplanner.com/tag/scoped_search
|
61
74
|
|
62
75
|
== License
|
63
76
|
|
64
|
-
This plugin is released under the MIT license. Please contact (
|
65
|
-
|
77
|
+
This plugin is released under the MIT license. Please contact weshays (http://github.com/weshays)
|
78
|
+
or wvanbergen (http://github.com/wvanbergen) for any questions.
|
data/Rakefile
CHANGED
@@ -1,43 +1,5 @@
|
|
1
1
|
Dir['tasks/*.rake'].each { |file| load(file) }
|
2
2
|
|
3
|
+
desc 'Default: run unit tests for only sqlite.'
|
3
4
|
task :default => [:test]
|
4
5
|
|
5
|
-
namespace :test do
|
6
|
-
|
7
|
-
desc "Run tests for all configured databases in test/database.yml"
|
8
|
-
task :all do
|
9
|
-
|
10
|
-
databases = YAML.load(File.read(File.dirname(__FILE__) + '/test/database.yml'))
|
11
|
-
databases.each do |database, config|
|
12
|
-
puts "\nRunning testsuite on #{database} database...\n\n"
|
13
|
-
sh "rake test DATABASE=#{database}"
|
14
|
-
end
|
15
|
-
puts "\nFinished testing for all configured databases!"
|
16
|
-
puts "(Configure databases by adjusting test/database.yml)"
|
17
|
-
end
|
18
|
-
|
19
|
-
task :single do
|
20
|
-
database = ENV['DATABASE'] || 'sqlite3'
|
21
|
-
puts "Running testsuite on #{database} database...\n"
|
22
|
-
sh "rake test DATABASE=#{database}"
|
23
|
-
end
|
24
|
-
|
25
|
-
desc "Run tests on SQLite3 database"
|
26
|
-
task :sqlite3 do
|
27
|
-
puts "Running testsuite on SQLite3 database...\n"
|
28
|
-
sh 'rake test DATABASE=sqlite3'
|
29
|
-
end
|
30
|
-
|
31
|
-
desc "Run tests on MySQL database"
|
32
|
-
task :mysql do
|
33
|
-
puts "Running testsuite on MySQL database...\n"
|
34
|
-
sh 'rake test DATABASE=mysql'
|
35
|
-
end
|
36
|
-
|
37
|
-
desc "Run tests on PostgrSQL database"
|
38
|
-
task :postgresql do
|
39
|
-
puts "Running testsuite on PostgreSQL database...\n"
|
40
|
-
sh 'rake test DATABASE=postgresql'
|
41
|
-
end
|
42
|
-
|
43
|
-
end
|
@@ -44,9 +44,7 @@ module ScopedSearch
|
|
44
44
|
search_conditions.each_with_index do |search_condition, index|
|
45
45
|
keyword_name = "keyword_#{index}".to_sym
|
46
46
|
conditions << case search_condition.last
|
47
|
-
#
|
48
|
-
# when :integer: integer_conditions(keyword_name, search_condition.first)
|
49
|
-
|
47
|
+
# :like also handles integers
|
50
48
|
when :like: like_condition(keyword_name, search_condition.first)
|
51
49
|
when :not: not_like_condition(keyword_name, search_condition.first)
|
52
50
|
|
@@ -68,17 +66,18 @@ module ScopedSearch
|
|
68
66
|
|
69
67
|
private
|
70
68
|
|
71
|
-
|
72
|
-
# Still thinking about this one
|
73
|
-
# end
|
74
|
-
|
75
|
-
def like_condition(keyword_name, value)
|
76
|
-
@query_params[keyword_name] = "%#{value}%"
|
69
|
+
def like_condition(keyword_name, value)
|
77
70
|
retVal = []
|
78
71
|
@query_fields.each do |field, field_type| #|key,value|
|
79
72
|
if field_type == :string or field_type == :text
|
73
|
+
@query_params[keyword_name] = "%#{value}%"
|
80
74
|
retVal << "#{field} #{@sql_like} :#{keyword_name.to_s}"
|
81
75
|
end
|
76
|
+
if value.strip =~ /^[0-9]+$/ and (field_type == :int or field_type == :integer)
|
77
|
+
qkey = "#{keyword_name}_#{value.strip}"
|
78
|
+
@query_params[qkey.to_sym] = value.strip.to_i
|
79
|
+
retVal << "#{field} = :#{qkey}"
|
80
|
+
end
|
82
81
|
end
|
83
82
|
"(#{retVal.join(' OR ')})"
|
84
83
|
end
|
@@ -101,10 +100,22 @@ module ScopedSearch
|
|
101
100
|
keyword_name_b = "#{keyword_name.to_s}b".to_sym
|
102
101
|
@query_params[keyword_name_a] = "%#{word1}%"
|
103
102
|
@query_params[keyword_name_b] = "%#{word2}%"
|
104
|
-
@query_fields.each do |field, field_type| #|key,value|
|
103
|
+
@query_fields.each do |field, field_type| #|key,value|
|
105
104
|
if field_type == :string or field_type == :text
|
106
105
|
retVal << "(#{field} #{@sql_like} :#{keyword_name_a.to_s} OR #{field} #{@sql_like} :#{keyword_name_b.to_s})"
|
107
106
|
end
|
107
|
+
if (word1.strip =~ /^[0-9]+$/ and word2.strip =~ /^[0-9]+$/) and (field_type == :int or field_type == :integer)
|
108
|
+
qkeya = "#{keyword_name}_a_#{word1.strip}"
|
109
|
+
qkeyb = "#{keyword_name}_b_#{word2.strip}"
|
110
|
+
@query_params[qkeya] = word1.strip.to_i
|
111
|
+
@query_params[qkeyb] = word2.strip.to_i
|
112
|
+
retVal << "(#{field} = :#{qkeya} OR #{field} = :#{qkeyb})"
|
113
|
+
elsif (word1.strip =~ /^[0-9]+$/ or word2.strip =~ /^[0-9]+$/) and (field_type == :int or field_type == :integer)
|
114
|
+
num_word = word1.strip =~ /^[0-9]+$/ ? word1.strip.to_i : word2.strip.to_i
|
115
|
+
qkey = "#{keyword_name}_#{num_word}"
|
116
|
+
@query_params[qkey.to_sym] = num_word
|
117
|
+
retVal << "(#{field} = :#{qkey})"
|
118
|
+
end
|
108
119
|
end
|
109
120
|
"(#{retVal.join(' OR ')})"
|
110
121
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'yaml' unless Object::const_defined?('YAML')
|
2
|
+
|
3
|
+
namespace :test do
|
4
|
+
|
5
|
+
databases = YAML.load(File.read(File.dirname(__FILE__) + '/../test/database.yml'))
|
6
|
+
|
7
|
+
desc "Run testsuite on all configured databases in test/database.yml"
|
8
|
+
task(:all => databases.keys.map { |db| db.to_sym }) do
|
9
|
+
puts "\nFinished testing on all configured databases!"
|
10
|
+
puts "(Configure databases by adjusting test/database.yml)"
|
11
|
+
end
|
12
|
+
|
13
|
+
databases.each do |database, config|
|
14
|
+
desc "Run testsuite on #{database} database."
|
15
|
+
task database.to_sym do
|
16
|
+
puts "Running testsuite on #{database} database...\n\n"
|
17
|
+
sh "rake test DATABASE=#{database}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake/rdoctask'
|
3
|
+
|
4
|
+
desc 'Generate documentation for the acts_as_callback_logger plugin.'
|
5
|
+
Rake::RDocTask.new do |rdoc|
|
6
|
+
rdoc.rdoc_dir = 'doc/html'
|
7
|
+
rdoc.title = 'scoped_search'
|
8
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
9
|
+
rdoc.main = 'README'
|
10
|
+
rdoc.rdoc_files.include('LICENSE',
|
11
|
+
'lib/')
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
begin
|
16
|
+
require 'rcov/rcovtask'
|
17
|
+
Rcov::RcovTask.new do |t|
|
18
|
+
t.test_files = Dir[ "test/**/*_test.rb" ]
|
19
|
+
end
|
20
|
+
rescue LoadError
|
21
|
+
nil
|
22
|
+
end
|
23
|
+
|
24
|
+
begin
|
25
|
+
require 'rcov/rcovtask'
|
26
|
+
desc 'Runs spec:rcov and then displays the coverage/index.html file in the browswer.'
|
27
|
+
task :rcov_display => [:clobber_rcov, :rcov] do
|
28
|
+
system("open coverage/index.html")
|
29
|
+
end
|
30
|
+
rescue LoadError
|
31
|
+
nil
|
32
|
+
end
|
33
|
+
|
@@ -35,10 +35,11 @@ class ScopedSearch::Test::API < Test::Unit::TestCase
|
|
35
35
|
def test_search_except_fields
|
36
36
|
Foo.searchable_on :except => [:id, :ignored_field, :created_at, :updated_at]
|
37
37
|
assert Foo.respond_to?(:search_for)
|
38
|
-
assert_equal Foo.scoped_search_fields.size,
|
38
|
+
assert_equal Foo.scoped_search_fields.size, 4
|
39
39
|
assert Foo.scoped_search_fields.include?(:string_field)
|
40
40
|
assert Foo.scoped_search_fields.include?(:text_field)
|
41
41
|
assert Foo.scoped_search_fields.include?(:date_field)
|
42
|
+
assert Foo.scoped_search_fields.include?(:some_int_field)
|
42
43
|
end
|
43
44
|
|
44
45
|
def test_search_with_only_and_except
|
data/test/lib/test_models.rb
CHANGED
@@ -2,22 +2,22 @@ module ScopedSearch::Test::Models
|
|
2
2
|
|
3
3
|
class Foo < ActiveRecord::Base
|
4
4
|
def self.create_corpus!
|
5
|
-
create!(:string_field => "Programmer 123", :text_field => nil, :ignored_field => "123456", :date_field => '2000-01-01')
|
6
|
-
create!(:string_field => "Jim", :text_field => "Henson", :ignored_field => "123456a", :date_field => '2001-04-15')
|
7
|
-
create!(:string_field => "Jim", :text_field => "Bush", :ignored_field => "123456b", :date_field => '2001-04-17')
|
8
|
-
create!(:string_field => "Wes", :text_field => "Hays", :ignored_field => "123456c", :date_field => '1980-09-27')
|
9
|
-
create!(:string_field => "Bob", :text_field => "Hays", :ignored_field => "123456d", :date_field => '2002-11-09')
|
10
|
-
create!(:string_field => "Dogs", :text_field => "Pit Bull", :ignored_field => "123456e", :date_field => '2002-12-26')
|
11
|
-
create!(:string_field => "Dogs", :text_field => "Eskimo", :ignored_field => "123456f", :date_field => '2003-03-19')
|
12
|
-
create!(:string_field => "Cows", :text_field => "Farms", :ignored_field => "123456g", :date_field => '2004-05-01')
|
13
|
-
create!(:string_field => "Hello World", :text_field => "Hello Moon", :ignored_field => "123456h", :date_field => '2004-07-11')
|
14
|
-
create!(:string_field => "Hello World", :text_field => "Goodnight Moon", :ignored_field => "123456i", :date_field => '2004-09-12')
|
15
|
-
create!(:string_field => "Happy Cow", :text_field => "Sad Cow", :ignored_field => "123456j", :date_field => '2005-02-05')
|
16
|
-
create!(:string_field => "Happy Frog", :text_field => "Sad Frog", :ignored_field => "123456k", :date_field => '2006-03-09')
|
17
|
-
create!(:string_field => "Excited Frog", :text_field => "Sad Frog", :ignored_field => "123456l", :date_field => '2006-07-15')
|
18
|
-
create!(:string_field => "Man made", :text_field => "Woman made", :ignored_field => "123456m", :date_field => '2007-06-13')
|
19
|
-
create!(:string_field => "Cat Toys", :text_field => "Frog Toys", :ignored_field => "123456n", :date_field => '2008-03-04')
|
20
|
-
create!(:string_field => "Happy Toys", :text_field => "Sad Toys", :ignored_field => "123456n", :date_field => '2008-05-12')
|
5
|
+
create!(:string_field => "Programmer 123", :text_field => nil, :ignored_field => "123456", :some_int_field => 111, :date_field => '2000-01-01')
|
6
|
+
create!(:string_field => "Jim", :text_field => "Henson", :ignored_field => "123456a", :some_int_field => 222, :date_field => '2001-04-15')
|
7
|
+
create!(:string_field => "Jim", :text_field => "Bush", :ignored_field => "123456b", :some_int_field => 333, :date_field => '2001-04-17')
|
8
|
+
create!(:string_field => "Wes", :text_field => "Hays", :ignored_field => "123456c", :some_int_field => 444, :date_field => '1980-09-27')
|
9
|
+
create!(:string_field => "Bob", :text_field => "Hays", :ignored_field => "123456d", :some_int_field => 555, :date_field => '2002-11-09')
|
10
|
+
create!(:string_field => "Dogs", :text_field => "Pit Bull", :ignored_field => "123456e", :some_int_field => 666, :date_field => '2002-12-26')
|
11
|
+
create!(:string_field => "Dogs", :text_field => "Eskimo", :ignored_field => "123456f", :some_int_field => 777, :date_field => '2003-03-19')
|
12
|
+
create!(:string_field => "Cows", :text_field => "Farms", :ignored_field => "123456g", :some_int_field => 888, :date_field => '2004-05-01')
|
13
|
+
create!(:string_field => "Hello World", :text_field => "Hello Moon", :ignored_field => "123456h", :some_int_field => 999, :date_field => '2004-07-11')
|
14
|
+
create!(:string_field => "Hello World", :text_field => "Goodnight Moon", :ignored_field => "123456i", :some_int_field => 100, :date_field => '2004-09-12')
|
15
|
+
create!(:string_field => "Happy Cow", :text_field => "Sad Cow", :ignored_field => "123456j", :some_int_field => 200, :date_field => '2005-02-05')
|
16
|
+
create!(:string_field => "Happy Frog", :text_field => "Sad Frog", :ignored_field => "123456k", :some_int_field => 300, :date_field => '2006-03-09')
|
17
|
+
create!(:string_field => "Excited Frog", :text_field => "Sad Frog", :ignored_field => "123456l", :some_int_field => 400, :date_field => '2006-07-15')
|
18
|
+
create!(:string_field => "Man made", :text_field => "Woman made", :ignored_field => "123456m", :some_int_field => 500, :date_field => '2007-06-13')
|
19
|
+
create!(:string_field => "Cat Toys", :text_field => "Frog Toys", :ignored_field => "123456n", :some_int_field => 600, :date_field => '2008-03-04')
|
20
|
+
create!(:string_field => "Happy Toys", :text_field => "Sad Toys", :ignored_field => "123456n", :some_int_field => 700, :date_field => '2008-05-12')
|
21
21
|
|
22
22
|
create!(:string_field => "My son was born on 7/15/2006 and weighed 5.5 lbs",
|
23
23
|
:text_field => "Sad Toys",
|
@@ -36,11 +36,11 @@ module ScopedSearch::Test::Models
|
|
36
36
|
has_many :clients, :through => :offices
|
37
37
|
|
38
38
|
def self.create_corpus!
|
39
|
-
create!(:first_name => 'Willem', :last_name => 'Van Bergen', :login => 'wvanbergen', :group_id => 1, :address_id => 1)
|
40
|
-
create!(:first_name => 'Wes', :last_name => 'Hays', :login => 'weshays', :group_id => 1, :address_id => 2)
|
41
|
-
create!(:first_name => 'John', :last_name => 'Dell', :login => 'jdell', :group_id => 2, :address_id => 3)
|
42
|
-
create!(:first_name => 'Ray', :last_name => 'York', :login => 'ryork', :group_id => 3, :address_id => 4)
|
43
|
-
create!(:first_name => 'Anna', :last_name => 'Landis', :login => 'alandis', :group_id => 4, :address_id => 5)
|
39
|
+
create!(:first_name => 'Willem', :last_name => 'Van Bergen', :login => 'wvanbergen', :age => 25, :group_id => 1, :address_id => 1)
|
40
|
+
create!(:first_name => 'Wes', :last_name => 'Hays', :login => 'weshays', :age => 26, :group_id => 1, :address_id => 2)
|
41
|
+
create!(:first_name => 'John', :last_name => 'Dell', :login => 'jdell', :age => 27, :group_id => 2, :address_id => 3)
|
42
|
+
create!(:first_name => 'Ray', :last_name => 'York', :login => 'ryork', :age => 28, :group_id => 3, :address_id => 4)
|
43
|
+
create!(:first_name => 'Anna', :last_name => 'Landis', :login => 'alandis', :age => 29, :group_id => 4, :address_id => 5)
|
44
44
|
|
45
45
|
user = self.find_by_first_name('Willem')
|
46
46
|
user.locations << ScopedSearch::Test::Models::Location.find_by_name('Office')
|
data/test/lib/test_schema.rb
CHANGED
@@ -8,12 +8,14 @@ class ScopedSearch::Test::DatabaseSchema < ActiveRecord::Migration
|
|
8
8
|
t.string :string_field
|
9
9
|
t.text :text_field
|
10
10
|
t.string :ignored_field
|
11
|
+
t.integer :some_int_field
|
11
12
|
t.date :date_field
|
12
13
|
t.timestamps
|
13
14
|
end
|
14
15
|
|
15
16
|
create_table :users do |t|
|
16
17
|
t.string :first_name, :last_name, :login
|
18
|
+
t.integer :age
|
17
19
|
t.integer :group_id
|
18
20
|
t.integer :address_id
|
19
21
|
end
|
@@ -35,6 +35,15 @@ class ScopedSearch::Test::QueryConditionsBuilder < Test::Unit::TestCase
|
|
35
35
|
assert_equal '%Wes%', conditions.last[:keyword_0]
|
36
36
|
end
|
37
37
|
|
38
|
+
def test_like_search_condition_with_integer
|
39
|
+
search_conditions = [["26", :like]]
|
40
|
+
query_fields = {'some_table.age' => :integer}
|
41
|
+
conditions = build_query(search_conditions, query_fields)
|
42
|
+
|
43
|
+
assert_equal '(some_table.age = :keyword_0_26)', conditions.first
|
44
|
+
assert_equal 26, conditions.last[:keyword_0_26]
|
45
|
+
end
|
46
|
+
|
38
47
|
def test_not_like_search_condition
|
39
48
|
search_conditions = [["Wes", :not]]
|
40
49
|
query_fields = {'some_table.first_name' => :string}
|
@@ -54,6 +63,16 @@ class ScopedSearch::Test::QueryConditionsBuilder < Test::Unit::TestCase
|
|
54
63
|
assert_equal '%Hays%', conditions.last[:keyword_0b]
|
55
64
|
end
|
56
65
|
|
66
|
+
def test_or_search_condition_with_integer
|
67
|
+
search_conditions = [["Wes OR 26", :or]]
|
68
|
+
query_fields = {'some_table.first_name' => :string, 'some_table.age' => :integer}
|
69
|
+
conditions = build_query(search_conditions, query_fields)
|
70
|
+
regExs = build_regex_for_or(['first_name', 'age'], 'keyword_0')
|
71
|
+
assert_match /^#{regExs}$/, conditions.first
|
72
|
+
assert_equal '%Wes%', conditions.last[:keyword_0a]
|
73
|
+
assert_equal 26, conditions.last[:keyword_0_26]
|
74
|
+
end
|
75
|
+
|
57
76
|
# ** less_than_date **
|
58
77
|
def test_less_than_date_search_condition_with_only_a_date_field_to_search
|
59
78
|
search_conditions = [['< 09/27/1980', :less_than_date]]
|
@@ -374,12 +393,13 @@ class ScopedSearch::Test::QueryConditionsBuilder < Test::Unit::TestCase
|
|
374
393
|
def build_regex_for_or(fields,keyword)
|
375
394
|
orFields = fields.join('|')
|
376
395
|
regParts = fields.collect { |field|
|
377
|
-
"[
|
396
|
+
"([(](some_table.(first_name|age) (LIKE|=) :keyword_0[a-zA-Z0-9_]+ OR )?some_table.(first_name|age) (LIKE|=) :keyword_0[a-zA-Z0-9_]+)[)]"
|
378
397
|
}.join('[ ]OR[ ]')
|
379
398
|
|
380
399
|
"[\(]#{regParts}[\)]"
|
381
400
|
end
|
382
401
|
|
402
|
+
|
383
403
|
def build_regex_for_date(fields,keyword)
|
384
404
|
orFields = fields.join('|')
|
385
405
|
regParts = fields.collect { |field|
|
@@ -30,12 +30,13 @@ class ScopedSearch::Test::QueryLanguage < Test::Unit::TestCase
|
|
30
30
|
assert_equal 2, parsed.length
|
31
31
|
assert_equal 'willem', parsed.last.first
|
32
32
|
|
33
|
-
parsed = parse_query(" hallo willem van\tbergen ")
|
34
|
-
assert_equal
|
33
|
+
parsed = parse_query(" hallo willem van\tbergen 25")
|
34
|
+
assert_equal 5, parsed.length
|
35
35
|
assert_equal 'hallo', parsed[0].first
|
36
36
|
assert_equal 'willem', parsed[1].first
|
37
37
|
assert_equal 'van', parsed[2].first
|
38
38
|
assert_equal 'bergen', parsed[3].first
|
39
|
+
assert_equal '25', parsed[4].first
|
39
40
|
end
|
40
41
|
|
41
42
|
def test_quoted_keywords
|
@@ -56,6 +57,10 @@ class ScopedSearch::Test::QueryLanguage < Test::Unit::TestCase
|
|
56
57
|
assert_equal 2, parsed.length
|
57
58
|
assert_equal 'hallo wi', parsed[0].first
|
58
59
|
assert_equal 'llem', parsed[1].first
|
60
|
+
|
61
|
+
parsed = parse_query('"hays 25"')
|
62
|
+
assert_equal 1, parsed.length
|
63
|
+
assert_equal 'hays 25', parsed[0].first
|
59
64
|
end
|
60
65
|
|
61
66
|
def test_quote_escaping
|
@@ -17,7 +17,7 @@ class ScopedSearch::Test::SearchFor < Test::Unit::TestCase
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def test_search
|
20
|
-
Foo.searchable_on :string_field, :text_field, :date_field
|
20
|
+
Foo.searchable_on :string_field, :text_field, :date_field, :some_int_field
|
21
21
|
|
22
22
|
assert_equal Foo.count, Foo.search_for('').count
|
23
23
|
assert_equal 0, Foo.search_for('456').count
|
@@ -32,6 +32,7 @@ class ScopedSearch::Test::SearchFor < Test::Unit::TestCase
|
|
32
32
|
assert_equal 3, Foo.search_for('"Happy cow" OR "Sad Frog"').count
|
33
33
|
assert_equal 3, Foo.search_for('"Man made" OR Dogs').count
|
34
34
|
assert_equal 2, Foo.search_for('Cows OR "Frog Toys"').count
|
35
|
+
assert_equal 1, Foo.search_for('700').count
|
35
36
|
|
36
37
|
# ** DATES **
|
37
38
|
#
|
@@ -83,12 +84,13 @@ class ScopedSearch::Test::SearchFor < Test::Unit::TestCase
|
|
83
84
|
end
|
84
85
|
|
85
86
|
def test_search_has_many_through_association
|
86
|
-
User.searchable_on :first_name, :last_name, :clients_first_name, :clients_last_name
|
87
|
+
User.searchable_on :first_name, :last_name, :age, :clients_first_name, :clients_last_name
|
87
88
|
|
88
89
|
assert_equal User.count, User.search_for('').count
|
89
90
|
assert_equal 2, User.search_for('Smith').count
|
90
91
|
assert_equal 1, User.search_for('Sam').count
|
91
92
|
assert_equal 1, User.search_for('Johnson').count
|
93
|
+
assert_equal 1, User.search_for('28').count
|
92
94
|
end
|
93
95
|
|
94
96
|
def test_search_has_one_association
|
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: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Willem van Bergen
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2009-
|
13
|
+
date: 2009-02-23 00:00:00 -08:00
|
14
14
|
default_executable:
|
15
15
|
dependencies: []
|
16
16
|
|
@@ -24,14 +24,10 @@ extensions: []
|
|
24
24
|
|
25
25
|
extra_rdoc_files:
|
26
26
|
- README.rdoc
|
27
|
-
- CHANGELOG.rdoc
|
28
|
-
- TODO.rdoc
|
29
27
|
files:
|
30
|
-
- CHANGELOG.rdoc
|
31
28
|
- LICENSE
|
32
29
|
- README.rdoc
|
33
30
|
- Rakefile
|
34
|
-
- TODO.rdoc
|
35
31
|
- init.rb
|
36
32
|
- lib
|
37
33
|
- lib/scoped_search
|
@@ -40,6 +36,8 @@ files:
|
|
40
36
|
- lib/scoped_search/query_language_parser.rb
|
41
37
|
- lib/scoped_search/reg_tokens.rb
|
42
38
|
- tasks
|
39
|
+
- tasks/database_tests.rake
|
40
|
+
- tasks/documentation.rake
|
43
41
|
- tasks/github-gem.rake
|
44
42
|
- test
|
45
43
|
- test/database.yml
|
data/CHANGELOG.rdoc
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
= Changelog for scoped_search
|
2
|
-
|
3
|
-
The following changes were incorporated in each scoped_search release.
|
4
|
-
|
5
|
-
== scoped_search 0.3.0
|
6
|
-
- Detection of column types so they can be handled properly
|
7
|
-
- Date based queries supported on date and time fields
|
8
|
-
|
9
|
-
== scoped_search 0.2.0
|
10
|
-
- OR keyword supported in query language
|
11
|
-
|
12
|
-
== scoped_search 0.1.0
|
13
|
-
- Initial version
|