wvanbergen-scoped_search 0.7.3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,60 @@
1
+ = scoped_search[http://github.com/wvanbergen/scoped_search/tree/master]
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 (ILIKE for Postgres). You can specify what fields should be used for searching.
4
+
5
+
6
+ == Installing
7
+
8
+ gem install wvanbergen-scoped_search
9
+
10
+
11
+ == Usage
12
+
13
+ First, you have to specify in what columns should be searched:
14
+
15
+ class User < ActiveRecord::Base
16
+ searchable_on :first_name, :last_name
17
+ end
18
+
19
+
20
+ Now, the <b>search_for</b> scope is available for queries. You should pass a query string to the scope.
21
+ This can be empty or nil, in which case all no search conditions are set (and all records will be returned).
22
+
23
+ User.search_for(params[:q]).each { |project| ... }
24
+
25
+
26
+ You can also search on associate models. This works with <b>belongs_to</b>, <b>has_one</b>, <b>has_many</b>,
27
+ <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)
28
+
29
+ class User < ActiveRecord::Base
30
+ has_many: notes
31
+ searchable_on :first_name, :last_name, :notes_title, :notes_content
32
+ end
33
+
34
+
35
+ The search query language is simple. It supports these constructs:
36
+ * <b>words:</b> some search keywords
37
+ * <b>phrases:</b> "a single search phrase"
38
+ * <b>negation:</b> "look for this" -"but do not look for this phrase and this" -word
39
+ * <b>OR words/phrases:</b> word/phrase OR word/phrase. Example: "Hello World" OR "Hello Moon"
40
+ * <b>dates:</b> mm/dd/yyyy, dd/mm/yyyy, yyyy/mm/dd, yyyy-mm-dd
41
+ * <b>date ranges:</b> > date, >= date, < date, <= date, date TO date. Examples: > mm/dd/yyyy, < yyyy-mm-dd
42
+
43
+ This functionality is build on named_scope. The searchable_on statement creates
44
+ a named_scope *search_for*. Because of this, you can actually chain the call with
45
+ other scopes. For example, this can be very useful if you only want to search in
46
+ projects that are accessible by a given user.
47
+
48
+ class Project < ActiveRecord::Base
49
+ searchable_on :name, :description
50
+ named_scope :accessible_by, lambda { |user| ... }
51
+ end
52
+
53
+ # using chained named_scopes and will_paginate
54
+ Project.accessible_by(current_user).search_for(params[:q]).paginate(:page => params[:page], :include => :tasks)
55
+
56
+
57
+ == License
58
+
59
+ This plugin is released under the MIT license. Please contact (willem AT vanbergen DOT org) if you have any suggestions or remarks.
60
+
data/Rakefile CHANGED
@@ -1,6 +1,25 @@
1
1
  require 'rubygems'
2
+ require 'rake/rdoctask'
2
3
 
3
4
  Dir['tasks/*.rake'].each { |file| load(file) }
4
5
 
5
6
  desc 'Default: run unit tests.'
6
- task :default => :test
7
+ task :default => [:test]
8
+
9
+ ##############################################
10
+ # Build RDocs
11
+ ##############################################
12
+ desc 'Generate documentation for the acts_as_callback_logger plugin.'
13
+ Rake::RDocTask.new do |rdoc|
14
+ rdoc.rdoc_dir = 'doc/html'
15
+ rdoc.title = 'scoped_search'
16
+ rdoc.options << '--line-numbers' << '--inline-source'
17
+ rdoc.main = 'README'
18
+ rdoc.rdoc_files.include('README',
19
+ 'CHANGELOG',
20
+ 'LICENSE',
21
+ 'TODO',
22
+ 'lib/')
23
+ end
24
+ ##############################################
25
+
@@ -1,13 +1,13 @@
1
1
  module ScopedSearch
2
2
 
3
3
  class QueryConditionsBuilder
4
- ## ActiveRecord::Base.connection.adapter_name
5
4
 
6
- # Build the query
5
+ # Builds the query string by calling the build method on a new instances of QueryConditionsBuilder.
7
6
  def self.build_query(search_conditions, query_fields)
8
7
  self.new.build(search_conditions, query_fields)
9
8
  end
10
9
 
10
+ # Initializes the default class variables.
11
11
  def initialize
12
12
  @query_fields = nil
13
13
  @query_params = {}
@@ -20,29 +20,32 @@ module ScopedSearch
20
20
  end
21
21
 
22
22
 
23
- # Build the query
23
+ # Build the query based on the search conditions and the fields to query.
24
24
  #
25
25
  # Hash query_options : A hash of fields and field types.
26
26
  #
27
- # Exampe:
28
- # search_conditions = [["Wes", :like], ["Hays", :not], ["Hello World", :like], ["Goodnight Moon", :not],
29
- # ["Bob OR Wes", :or], ["Happy cow OR Sad Frog", :or], ["Man made OR Dogs", :or],
30
- # ["Cows OR Frog Toys", :or], ['9/28/1980, :datetime]]
31
- # query_fields = {:first_name => :string, :created_at => :datetime}
32
- #
27
+ # Example:
28
+ #
29
+ # search_conditions = [["Wes", :like], ["Hays", :not], ["Hello World", :like], ["Goodnight Moon", :not],
30
+ # ["Bob OR Wes", :or], ["Happy cow OR Sad Frog", :or], ["Man made OR Dogs", :or],
31
+ # ["Cows OR Frog Toys", :or], ['9/28/1980, :datetime]]
32
+ # query_fields = {:first_name => :string, :created_at => :datetime}
33
+ #
33
34
  # Exceptons :
34
- # 1) If urlParams does not contain a :controller key.
35
+ # 1) If search_conditions is not an array
36
+ # 2) If query_fields is not a Hash
35
37
  def build(search_conditions, query_fields)
36
38
  raise 'search_conditions must be a hash' unless search_conditions.class.to_s == 'Array'
37
39
  raise 'query_fields must be a hash' unless query_fields.class.to_s == 'Hash'
38
40
  @query_fields = query_fields
39
41
 
40
42
  conditions = []
41
-
43
+
42
44
  search_conditions.each_with_index do |search_condition, index|
43
45
  keyword_name = "keyword_#{index}".to_sym
44
46
  conditions << case search_condition.last
45
- #when :integer: integer_conditions(keyword_name, search_condition.first)
47
+ # Still thinking about this one
48
+ # when :integer: integer_conditions(keyword_name, search_condition.first)
46
49
 
47
50
  when :like: like_condition(keyword_name, search_condition.first)
48
51
  when :not: not_like_condition(keyword_name, search_condition.first)
@@ -66,6 +69,7 @@ module ScopedSearch
66
69
  private
67
70
 
68
71
  # def integer_condition(keyword_name, value)
72
+ # Still thinking about this one
69
73
  # end
70
74
 
71
75
  def like_condition(keyword_name, value)
@@ -125,20 +129,25 @@ module ScopedSearch
125
129
  end
126
130
  rescue
127
131
  # do not search on any date columns since the date is invalid
132
+ retVal = [] # Reset just in case
128
133
  end
129
134
 
130
135
  # Search the text fields for the date as well as it could be in text.
131
136
  # Also still search on the text columns for an invalid date as it could
132
137
  # have a different meaning.
133
- keyword_name_b = "#{keyword_name}b".to_sym
134
- @query_params[keyword_name_b] = "%#{value}%"
138
+ found_text_fields_to_search = false
139
+ keyword_name_b = "#{keyword_name}b".to_sym
135
140
  @query_fields.each do |field, field_type| #|key,value|
136
141
  if field_type == :string or field_type == :text
142
+ found_text_fields_to_search = true
137
143
  retVal << "#{field} #{@sql_like} :#{keyword_name_b.to_s}"
138
144
  end
139
145
  end
146
+ if found_text_fields_to_search
147
+ @query_params[keyword_name_b] = "%#{value}%"
148
+ end
140
149
 
141
- "(#{retVal.join(' OR ')})"
150
+ retVal.empty? ? '' : "(#{retVal.join(' OR ')})"
142
151
  end
143
152
 
144
153
  def greater_than_date(keyword_name, value)
@@ -1,17 +1,33 @@
1
1
  module ScopedSearch
2
2
 
3
+ # Used to parse and build the conditions tree for the search.
3
4
  class QueryLanguageParser
4
5
 
5
- def parse_query(query = nil)
6
- return build_conditions_tree(tokenize(query))
7
- end
8
-
6
+ # Parses the query string by calling the parse_query method on a new instances of QueryLanguageParser.
7
+ #
8
+ # query:: The query string to parse. If nil the all values will be returned (default is nil)
9
+ # If the query string is longer then 300 characters then it will be truncated to a
10
+ # length of 300.
9
11
  def self.parse(query)
10
12
  self.new.parse_query(query)
13
+ end
14
+
15
+ # Parse the query string.
16
+ #
17
+ # query:: The query string to parse. If nil the all values will be returned (default is nil)
18
+ # If the query string is longer then 300 characters then it will be truncated to a
19
+ # length of 300.
20
+ def parse_query(query = nil)
21
+ # truncate query string at 300 characters
22
+ query = query[0...300] if query.length > 300
23
+ return build_conditions_tree(tokenize(query))
11
24
  end
12
25
 
13
26
  protected
14
27
 
28
+ # Build the conditions tree based on the tokens found.
29
+ #
30
+ # tokens:: An array of tokens.
15
31
  def build_conditions_tree(tokens)
16
32
  conditions_tree = []
17
33
 
@@ -57,6 +73,7 @@ module ScopedSearch
57
73
  return conditions_tree
58
74
  end
59
75
 
76
+ # Tokenize the query based on the different RegTokens.
60
77
  def tokenize(query)
61
78
  pattern = [RegTokens::BetweenDateFormatMMDDYYYY,
62
79
  RegTokens::BetweenDateFormatYYYYMMDD,
data/lib/scoped_search.rb CHANGED
@@ -1,30 +1,51 @@
1
1
  module ScopedSearch
2
2
 
3
3
  module ClassMethods
4
-
5
- def self.extended(base)
4
+
5
+ def self.extended(base) # :nodoc:
6
6
  require 'scoped_search/reg_tokens'
7
7
  require 'scoped_search/query_language_parser'
8
8
  require 'scoped_search/query_conditions_builder'
9
9
  end
10
10
 
11
- # Creates a named scope in the class it was called upon
11
+ # Creates a named scope in the class it was called upon.
12
+ #
13
+ # fields:: The fields to search on.
12
14
  def searchable_on(*fields)
13
15
  # Make sure that the table to be searched actually exists
14
16
  if self.table_exists?
17
+
18
+ # Get a collection of fields to be searched on.
15
19
  if fields.first.class.to_s == 'Hash'
16
20
  if fields.first.has_key?(:only)
21
+ # only search on these fields.
17
22
  fields = fields.first[:only]
18
23
  elsif fields.first.has_key?(:except)
19
- fields = self.column_names.collect { |column|
20
- fields.first[:except].include?(column.to_sym) ? nil : column.to_sym }.compact
24
+ # Get all the fields and remove any that are in the -except- list.
25
+ fields = self.column_names.collect { |column| fields.first[:except].include?(column.to_sym) ? nil : column.to_sym }.compact
21
26
  end
22
27
  end
23
28
 
24
- assoc_models = self.reflections.collect { |m| m[0] }
29
+ # Get an array of associate modules.
30
+ assoc_models = self.reflections.collect { |key,value| key }
31
+
32
+ # Subtract out the fields to be searched on that are part of *this* model.
33
+ # Any thing left will be associate module fields to be searched on.
25
34
  assoc_fields = fields - self.column_names.collect { |column| column.to_sym }
35
+
36
+ # Subtraced out the associated fields from the fields so that you are only left
37
+ # with fields in *this* model.
26
38
  fields -= assoc_fields
27
39
 
40
+ # Loop through each of the associate models and group accordingly each
41
+ # associate model field to search. Assuming the following relations:
42
+ # has_many :clients
43
+ # has_many :notes,
44
+ # belongs_to :user_type
45
+ # assoc_groupings will look like
46
+ # assoc_groupings = {:clients => [:first_name, :last_name],
47
+ # :notes => [:descr],
48
+ # :user_type => [:identifier]}
28
49
  assoc_groupings = {}
29
50
  assoc_models.each do |assoc_model|
30
51
  assoc_groupings[assoc_model] = []
@@ -34,9 +55,11 @@ module ScopedSearch
34
55
  end
35
56
  end
36
57
  end
37
-
58
+
59
+ # If a grouping does not contain any fields to be searched on then remove it.
38
60
  assoc_groupings = assoc_groupings.delete_if {|group, field_group| field_group.empty?}
39
61
 
62
+ # Set the appropriate class attributes.
40
63
  self.cattr_accessor :scoped_search_fields, :scoped_search_assoc_groupings
41
64
  self.scoped_search_fields = fields
42
65
  self.scoped_search_assoc_groupings = assoc_groupings
@@ -46,7 +69,9 @@ module ScopedSearch
46
69
 
47
70
  # Build a hash that is used for the named_scope search_for.
48
71
  # This function will split the search_string into keywords, and search for all the keywords
49
- # in the fields that were provided to searchable_on
72
+ # in the fields that were provided to searchable_on.
73
+ #
74
+ # search_string:: The search string to parse.
50
75
  def build_scoped_search_conditions(search_string)
51
76
  if search_string.nil? || search_string.strip.blank?
52
77
  return {:conditions => nil}
@@ -57,18 +82,20 @@ module ScopedSearch
57
82
  query_fields[field_name] = self.columns_hash[field.to_s].type
58
83
  end
59
84
 
85
+ assoc_model_indx = 0
86
+ assoc_fields_indx = 1
60
87
  assoc_models_to_include = []
61
- self.scoped_search_assoc_groupings.each do |group|
62
- assoc_models_to_include << group[0]
63
- group[1].each do |group_field|
64
- field_name = connection.quote_table_name(group[0].to_s.pluralize) + "." + connection.quote_column_name(group_field)
65
- query_fields[field_name] = self.reflections[group[0]].klass.columns_hash[group_field.to_s].type
88
+ self.scoped_search_assoc_groupings.each do |group|
89
+ assoc_models_to_include << group[assoc_model_indx]
90
+ group[assoc_fields_indx].each do |group_field|
91
+ field_name = connection.quote_table_name(group[assoc_model_indx].to_s.pluralize) + "." + connection.quote_column_name(group_field)
92
+ query_fields[field_name] = self.reflections[group[assoc_model_indx]].klass.columns_hash[group_field.to_s].type
66
93
  end
67
94
  end
68
95
 
69
96
  search_conditions = QueryLanguageParser.parse(search_string)
70
97
  conditions = QueryConditionsBuilder.build_query(search_conditions, query_fields)
71
-
98
+
72
99
  retVal = {:conditions => conditions}
73
100
  retVal[:include] = assoc_models_to_include unless assoc_models_to_include.empty?
74
101
 
@@ -1,4 +1,5 @@
1
1
  require 'rubygems'
2
+ require 'rubyforge'
2
3
  require 'rake'
3
4
  require 'rake/tasklib'
4
5
  require 'date'
@@ -24,23 +25,16 @@ module Rake
24
25
  namespace(:gem) do
25
26
  desc "Updates the file lists for this gem"
26
27
  task(:manifest) { manifest_task }
27
-
28
- desc "Builds a ruby gem for #{@name}"
29
- task(:build => [:manifest]) { build_task }
30
-
31
- desc "Installs the ruby gem for #{@name} locally"
32
- task(:install => [:build]) { install_task }
33
-
34
- desc "Uninstalls the ruby gem for #{@name} locally"
35
- task(:uninstall) { uninstall_task }
28
+
29
+ desc "Builds a .gem package for #{@name}"
30
+ task(:build) { build_task }
36
31
 
37
32
  desc "Releases a new version of #{@name}"
38
33
  task(:release) { release_task }
39
- end
34
+
35
+ end
40
36
  end
41
37
 
42
-
43
-
44
38
  protected
45
39
 
46
40
  def reload_gemspec!
@@ -135,7 +129,7 @@ module Rake
135
129
  spec = File.read(gemspec_file)
136
130
  spec.gsub! /^(\s* s.(test_)?files \s* = \s* )( \[ [^\]]* \] | %w\( [^)]* \) )/mx do
137
131
  assignment = $1
138
- bunch = $2 ? list.grep(/^test.*_test\.rb$/) : list
132
+ bunch = $2 ? list.grep(/^(test.*_test\.rb|spec.*_spec.rb)$/) : list
139
133
  '%s%%w(%s)' % [assignment, bunch.join(' ')]
140
134
  end
141
135
 
@@ -145,15 +139,17 @@ module Rake
145
139
 
146
140
  def build_task
147
141
  sh "gem build #{gemspec_file}"
142
+ Dir.mkdir('pkg') unless File.exist?('pkg')
143
+ sh "mv #{name}-#{specification.version}.gem pkg/#{name}-#{specification.version}.gem"
148
144
  end
149
145
 
150
146
  def install_task
151
- raise "#{name} .gem file not found" unless File.exist?("#{name}-#{specification.version}.gem")
152
- sh "gem install #{name}-#{specification.version}.gem"
147
+ raise "#{name} .gem file not found" unless File.exist?("pkg/#{name}-#{specification.version}.gem")
148
+ sh "gem install pkg/#{name}-#{specification.version}.gem"
153
149
  end
154
150
 
155
151
  def uninstall_task
156
- raise "#{name} .gem file not found" unless File.exist?("#{name}-#{specification.version}.gem")
152
+ raise "#{name} .gem file not found" unless File.exist?("pkg/#{name}-#{specification.version}.gem")
157
153
  sh "gem uninstall #{name}"
158
154
  end
159
155
 
data/tasks/test.rake CHANGED
@@ -10,4 +10,23 @@ Rake::TestTask.new(:test) do |t|
10
10
  # is sqlite3 if not specified or if the parameter is invalid.
11
11
  # If DATABASE is mysql then the MYSQLSOCKET can also be set if needed.
12
12
  ENV['DATABASE'] = ENV['DATABASE'].nil? ? 'sqlite3' : ENV['DATABASE'].downcase
13
- end
13
+ end
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
@@ -7,12 +7,30 @@ class QueryConditionsBuilderTest < Test::Unit::TestCase
7
7
  ScopedSearch::QueryConditionsBuilder.build_query(search_conditions, query_fields)
8
8
  end
9
9
 
10
+ # ** Invalid search conditions **
11
+ def test_search_with_invalid_search_conditions
12
+ search_conditions = ''
13
+ query_fields = {'some_table.first_name' => :string}
14
+ assert_raise(RuntimeError, 'search_conditions must be a hash') {
15
+ build_query(search_conditions, query_fields)
16
+ }
17
+ end
18
+
19
+ def test_search_with_invalid_query_fields
20
+ search_conditions = [["Wes", :like]]
21
+ query_fields = ''
22
+ assert_raise(RuntimeError, 'query_fields must be a hash') {
23
+ build_query(search_conditions, query_fields)
24
+ }
25
+ end
26
+
27
+
10
28
  # ** Single query search tests **
11
29
  def test_like_search_condition
12
30
  search_conditions = [["Wes", :like]]
13
31
  query_fields = {'some_table.first_name' => :string}
14
32
  conditions = build_query(search_conditions, query_fields)
15
-
33
+
16
34
  assert_equal '(some_table.first_name LIKE :keyword_0)', conditions.first
17
35
  assert_equal '%Wes%', conditions.last[:keyword_0]
18
36
  end
@@ -36,14 +54,273 @@ class QueryConditionsBuilderTest < Test::Unit::TestCase
36
54
  assert_equal '%Hays%', conditions.last[:keyword_0b]
37
55
  end
38
56
 
39
- # def test_date_search_condition
40
- # search_conditions = [["09/27/1980", :as_of_date]]
41
- # query_fields = {'some_table.event_date' => :datetime}
42
- # conditions = build_query(search_conditions, query_fields)
43
- # regExs = build_regex_for_date(['event_date'], 'keyword_0')
44
- # assert_match /^#{regExs}$/, conditions.first
45
- # assert_equal '09/27/1980', conditions.last[:keyword_0a]
46
- # end
57
+ # ** less_than_date **
58
+ def test_less_than_date_search_condition_with_only_a_date_field_to_search
59
+ search_conditions = [['< 09/27/1980', :less_than_date]]
60
+ query_fields = {'some_table.event_date' => :datetime}
61
+ conditions = build_query(search_conditions, query_fields)
62
+
63
+ assert_equal '(some_table.event_date < :keyword_0)', conditions.first
64
+ assert_equal '1980-09-27', conditions.last[:keyword_0]
65
+ end
66
+
67
+ def test_less_than_date_search_condition_with_invalid_date
68
+ search_conditions = [['< 2/30/1980', :less_than_date]]
69
+ query_fields = {'some_table.event_date' => :datetime}
70
+ conditions = build_query(search_conditions, query_fields)
71
+
72
+ assert conditions.first.empty?
73
+ assert conditions.last.empty?
74
+ end
75
+
76
+ def test_less_than_date_search_condition_with_a_date_field_and_a_text_field
77
+ search_conditions = [['< 09/27/1980', :less_than_date]]
78
+ query_fields = {'some_table.event_date' => :datetime, 'some_table.first_name' => :string}
79
+ conditions = build_query(search_conditions, query_fields)
80
+
81
+ assert_equal '(some_table.event_date < :keyword_0)', conditions.first
82
+ assert_equal '1980-09-27', conditions.last[:keyword_0]
83
+ end
84
+
85
+ def test_less_than_date_search_condition_with_a_date_field_and_a_text_field
86
+ search_conditions = [['< 2/30/1980', :less_than_date]]
87
+ query_fields = {'some_table.event_date' => :datetime, 'some_table.first_name' => :string}
88
+ conditions = build_query(search_conditions, query_fields)
89
+
90
+ assert conditions.first.empty?
91
+ assert conditions.last.empty?
92
+ end
93
+
94
+
95
+ # ** less_than_or_equal_to_date **
96
+ def test_less_than_or_equal_to_date_search_condition_with_only_a_date_field_to_search
97
+ search_conditions = [['<= 09/27/1980', :less_than_or_equal_to_date]]
98
+ query_fields = {'some_table.event_date' => :datetime}
99
+ conditions = build_query(search_conditions, query_fields)
100
+
101
+ assert_equal '(some_table.event_date <= :keyword_0)', conditions.first
102
+ assert_equal '1980-09-27', conditions.last[:keyword_0]
103
+ end
104
+
105
+ def test_less_than_or_equal_to_date_search_condition_with_invalid_date
106
+ search_conditions = [['<= 2/30/1980', :less_than_or_equal_to_date]]
107
+ query_fields = {'some_table.event_date' => :datetime}
108
+ conditions = build_query(search_conditions, query_fields)
109
+
110
+ assert conditions.first.empty?
111
+ assert conditions.last.empty?
112
+ end
113
+
114
+ def test_less_than_or_equal_to_date_search_condition_with_a_date_field_and_a_text_field
115
+ search_conditions = [['<= 09/27/1980', :less_than_or_equal_to_date]]
116
+ query_fields = {'some_table.event_date' => :datetime, 'some_table.first_name' => :string}
117
+ conditions = build_query(search_conditions, query_fields)
118
+
119
+ assert_equal '(some_table.event_date <= :keyword_0)', conditions.first
120
+ assert_equal '1980-09-27', conditions.last[:keyword_0]
121
+ end
122
+
123
+ def test_less_than_or_equal_to_date_search_condition_with_a_date_field_and_a_text_field
124
+ search_conditions = [['<= 2/30/1980', :less_than_or_equal_to_date]]
125
+ query_fields = {'some_table.event_date' => :datetime, 'some_table.first_name' => :string}
126
+ conditions = build_query(search_conditions, query_fields)
127
+
128
+ assert conditions.first.empty?
129
+ assert conditions.last.empty?
130
+ end
131
+
132
+
133
+ # ** as_of_date **
134
+ def test_as_of_date_search_condition_with_only_a_date_field_to_search
135
+ search_conditions = [['09/27/1980', :as_of_date]]
136
+ query_fields = {'some_table.event_date' => :datetime}
137
+ conditions = build_query(search_conditions, query_fields)
138
+
139
+ assert_equal '(some_table.event_date = :keyword_0)', conditions.first
140
+ assert_equal '1980-09-27', conditions.last[:keyword_0]
141
+ end
142
+
143
+ def test_as_of_date_search_condition_with_invalid_date
144
+ search_conditions = [['2/30/1980', :as_of_date]]
145
+ query_fields = {'some_table.event_date' => :datetime}
146
+ conditions = build_query(search_conditions, query_fields)
147
+
148
+ assert conditions.first.empty?
149
+ assert conditions.last.empty?
150
+ end
151
+
152
+ def test_as_of_date_search_condition_with_a_date_field_and_a_text_field
153
+ search_conditions = [['09/27/1980', :as_of_date]]
154
+ query_fields = {'some_table.event_date' => :datetime, 'some_table.first_name' => :string}
155
+ conditions = build_query(search_conditions, query_fields)
156
+
157
+ assert_equal '(some_table.event_date = :keyword_0 OR some_table.first_name LIKE :keyword_0b)', conditions.first
158
+ assert_equal '1980-09-27', conditions.last[:keyword_0]
159
+ assert_equal '%09/27/1980%', conditions.last[:keyword_0b]
160
+ end
161
+
162
+ def test_as_of_date_search_condition_with_a_date_field_and_a_text_field
163
+ search_conditions = [['2/30/1980', :as_of_date]]
164
+ query_fields = {'some_table.event_date' => :datetime, 'some_table.first_name' => :string}
165
+ conditions = build_query(search_conditions, query_fields)
166
+
167
+ assert_equal '(some_table.first_name LIKE :keyword_0b)', conditions.first
168
+ assert_equal '%2/30/1980%', conditions.last[:keyword_0b]
169
+ end
170
+
171
+
172
+ # ** greater_than_date **
173
+ def test_less_than_or_equal_to_date_search_condition_with_only_a_date_field_to_search
174
+ search_conditions = [['> 09/27/1980', :greater_than_date]]
175
+ query_fields = {'some_table.event_date' => :datetime}
176
+ conditions = build_query(search_conditions, query_fields)
177
+
178
+ assert_equal '(some_table.event_date > :keyword_0)', conditions.first
179
+ assert_equal '1980-09-27', conditions.last[:keyword_0]
180
+ end
181
+
182
+ def test_less_than_or_equal_to_date_search_condition_with_invalid_date
183
+ search_conditions = [['> 2/30/1980', :greater_than_date]]
184
+ query_fields = {'some_table.event_date' => :datetime}
185
+ conditions = build_query(search_conditions, query_fields)
186
+
187
+ assert conditions.first.empty?
188
+ assert conditions.last.empty?
189
+ end
190
+
191
+ def test_less_than_or_equal_to_date_search_condition_with_a_date_field_and_a_text_field
192
+ search_conditions = [['> 09/27/1980', :greater_than_date]]
193
+ query_fields = {'some_table.event_date' => :datetime, 'some_table.first_name' => :string}
194
+ conditions = build_query(search_conditions, query_fields)
195
+
196
+ assert_equal '(some_table.event_date > :keyword_0)', conditions.first
197
+ assert_equal '1980-09-27', conditions.last[:keyword_0]
198
+ end
199
+
200
+ def test_less_than_or_equal_to_date_search_condition_with_a_date_field_and_a_text_field
201
+ search_conditions = [['> 2/30/1980', :greater_than_date]]
202
+ query_fields = {'some_table.event_date' => :datetime, 'some_table.first_name' => :string}
203
+ conditions = build_query(search_conditions, query_fields)
204
+
205
+ assert conditions.first.empty?
206
+ assert conditions.last.empty?
207
+ end
208
+
209
+
210
+ # ** greater_than_or_equal_to_date **
211
+ def test_less_than_or_equal_to_date_search_condition_with_only_a_date_field_to_search
212
+ search_conditions = [['>= 09/27/1980', :greater_than_or_equal_to_date]]
213
+ query_fields = {'some_table.event_date' => :datetime}
214
+ conditions = build_query(search_conditions, query_fields)
215
+
216
+ assert_equal '(some_table.event_date >= :keyword_0)', conditions.first
217
+ assert_equal '1980-09-27', conditions.last[:keyword_0]
218
+ end
219
+
220
+ def test_less_than_or_equal_to_date_search_condition_with_invalid_date
221
+ search_conditions = [['>= 2/30/1980', :greater_than_or_equal_to_date]]
222
+ query_fields = {'some_table.event_date' => :datetime}
223
+ conditions = build_query(search_conditions, query_fields)
224
+
225
+ assert conditions.first.empty?
226
+ assert conditions.last.empty?
227
+ end
228
+
229
+ def test_less_than_or_equal_to_date_search_condition_with_a_date_field_and_a_text_field
230
+ search_conditions = [['>= 09/27/1980', :greater_than_or_equal_to_date]]
231
+ query_fields = {'some_table.event_date' => :datetime, 'some_table.first_name' => :string}
232
+ conditions = build_query(search_conditions, query_fields)
233
+
234
+ assert_equal '(some_table.event_date >= :keyword_0)', conditions.first
235
+ assert_equal '1980-09-27', conditions.last[:keyword_0]
236
+ end
237
+
238
+ def test_less_than_or_equal_to_date_search_condition_with_a_date_field_and_a_text_field
239
+ search_conditions = [['>= 2/30/1980', :greater_than_or_equal_to_date]]
240
+ query_fields = {'some_table.event_date' => :datetime, 'some_table.first_name' => :string}
241
+ conditions = build_query(search_conditions, query_fields)
242
+
243
+ assert conditions.first.empty?
244
+ assert conditions.last.empty?
245
+ end
246
+
247
+
248
+
249
+ # ** between_dates **
250
+ def test_between_dates_search_condition_two_valid_dates
251
+ search_conditions = [['09/27/1980 TO 10/15/1980', :between_dates]]
252
+ query_fields = {'some_table.event_date' => :datetime}
253
+ conditions = build_query(search_conditions, query_fields)
254
+
255
+ assert_equal '((some_table.event_date BETWEEN :keyword_0a AND :keyword_0b))', conditions.first
256
+ assert_equal '1980-09-27', conditions.last[:keyword_0a]
257
+ assert_equal '1980-10-15', conditions.last[:keyword_0b]
258
+ end
259
+
260
+ def test_between_dates_search_condition_with_a_valid_date_first_and_an_invalid_date_second
261
+ search_conditions = [['09/27/1980 TO 2/30/1981', :between_dates]]
262
+ query_fields = {'some_table.event_date' => :datetime}
263
+ conditions = build_query(search_conditions, query_fields)
264
+
265
+ assert conditions.first.empty?
266
+ assert conditions.last.empty?
267
+ end
268
+
269
+ def test_between_dates_search_condition_with_an_invalid_date_first_and_a_valid_date_second
270
+ search_conditions = [['02/30/1980 TO 09/27/1980', :between_dates]]
271
+ query_fields = {'some_table.event_date' => :datetime}
272
+ conditions = build_query(search_conditions, query_fields)
273
+
274
+ assert conditions.first.empty?
275
+ assert conditions.last.empty?
276
+ end
277
+
278
+ def test_between_dates_search_condition_with_two_invalid_dates
279
+ search_conditions = [['02/30/1980 TO 02/30/1981', :between_dates]]
280
+ query_fields = {'some_table.event_date' => :datetime}
281
+ conditions = build_query(search_conditions, query_fields)
282
+
283
+ assert conditions.first.empty?
284
+ assert conditions.last.empty?
285
+ end
286
+
287
+
288
+ def test_between_dates_search_condition_two_valid_dates_and_a_text_field
289
+ search_conditions = [['09/27/1980 TO 10/15/1980', :between_dates]]
290
+ query_fields = {'some_table.event_date' => :datetime, 'some_table.first_name' => :string}
291
+ conditions = build_query(search_conditions, query_fields)
292
+
293
+ assert_equal '((some_table.event_date BETWEEN :keyword_0a AND :keyword_0b))', conditions.first
294
+ assert_equal '1980-09-27', conditions.last[:keyword_0a]
295
+ assert_equal '1980-10-15', conditions.last[:keyword_0b]
296
+ end
297
+
298
+ def test_between_dates_search_condition_with_a_valid_date_first_and_an_invalid_date_second_and_a_text_field
299
+ search_conditions = [['09/27/1980 TO 2/30/1981', :between_dates]]
300
+ query_fields = {'some_table.event_date' => :datetime, 'some_table.first_name' => :string}
301
+ conditions = build_query(search_conditions, query_fields)
302
+
303
+ assert conditions.first.empty?
304
+ assert conditions.last.empty?
305
+ end
306
+
307
+ def test_between_dates_search_condition_with_an_invalid_date_first_and_a_valid_date_second_and_a_text_field
308
+ search_conditions = [['02/30/1980 TO 09/27/1980', :between_dates]]
309
+ query_fields = {'some_table.event_date' => :datetime, 'some_table.first_name' => :string}
310
+ conditions = build_query(search_conditions, query_fields)
311
+
312
+ assert conditions.first.empty?
313
+ assert conditions.last.empty?
314
+ end
315
+
316
+ def test_between_dates_search_condition_with_two_invalid_dates_and_a_text_field
317
+ search_conditions = [['02/30/1980 TO 02/30/1981', :between_dates]]
318
+ query_fields = {'some_table.event_date' => :datetime, 'some_table.first_name' => :string}
319
+ conditions = build_query(search_conditions, query_fields)
320
+
321
+ assert conditions.first.empty?
322
+ assert conditions.last.empty?
323
+ end
47
324
 
48
325
 
49
326
  # ** Multi query search tests **
@@ -76,7 +353,6 @@ class QueryConditionsBuilderTest < Test::Unit::TestCase
76
353
  end
77
354
 
78
355
 
79
-
80
356
  # ** Helper methods **
81
357
  def build_regex_for_like(fields,keyword)
82
358
  orFields = fields.join('|')
@@ -118,28 +118,6 @@ class QueryLanguageTest < Test::Unit::TestCase
118
118
  assert_equal :or, parsed[0][1]
119
119
  end
120
120
 
121
- def test_as_of_date
122
- # parsed = parse_query('9/27/1980')
123
- # assert_equal 1, parsed.length
124
- # assert_equal '9/27/1980', parsed[0][0]
125
- # assert_equal :as_of, parsed[0][1]
126
-
127
- # parsed = parse_query('"Man made" OR Dogs')
128
- # assert_equal 1, parsed.length
129
- # assert_equal 'Man made OR Dogs', parsed[0][0]
130
- # assert_equal :or, parsed[0][1]
131
- #
132
- # parsed = parse_query('Cows OR "Frog Toys"')
133
- # assert_equal 1, parsed.length
134
- # assert_equal 'Cows OR Frog Toys', parsed[0][0]
135
- # assert_equal :or, parsed[0][1]
136
- #
137
- # parsed = parse_query('"Happy cow" OR "Sad Frog"')
138
- # assert_equal 1, parsed.length
139
- # assert_equal 'Happy cow OR Sad Frog', parsed[0][0]
140
- # assert_equal :or, parsed[0][1]
141
- end
142
-
143
121
  def test_long_string
144
122
  str = 'Wes -Hays "Hello World" -"Goodnight Moon" Bob OR Wes "Happy cow" OR "Sad Frog" "Man made" OR Dogs Cows OR "Frog Toys"'
145
123
  parsed = parse_query(str)
@@ -153,6 +153,13 @@ class ScopedSearchTest < Test::Unit::TestCase
153
153
  assert_equal 1, User.search_for('Store').count
154
154
  assert_equal 1, User.search_for('John Office').count
155
155
  end
156
+
157
+ def test_search_with_very_long_query
158
+ User.searchable_on :first_name, :last_name, :address_street, :address_city, :address_state, :address_postal_code
159
+ really_long_string = ''
160
+ 10000.times {really_long_string << 'really long string'}
161
+ assert_equal 0, User.search_for(really_long_string).count
162
+ end
156
163
 
157
164
  end
158
165
 
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.7.3
4
+ version: 1.0.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: 2008-11-24 00:00:00 -08:00
13
+ date: 2009-01-15 00:00:00 -08:00
14
14
  default_executable:
15
15
  dependencies: []
16
16
 
@@ -27,9 +27,11 @@ extra_rdoc_files: []
27
27
  files:
28
28
  - CHANGELOG
29
29
  - LICENSE
30
+ - README
30
31
  - README.textile
31
32
  - Rakefile
32
33
  - TODO
34
+ - coverage
33
35
  - init.rb
34
36
  - lib
35
37
  - lib/scoped_search