meta_search 0.3.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitmodules +6 -0
- data/Gemfile +7 -0
- data/README.rdoc +165 -26
- data/Rakefile +18 -5
- data/VERSION +1 -1
- data/lib/meta_search.rb +28 -18
- data/lib/meta_search/builder.rb +178 -125
- data/lib/meta_search/exceptions.rb +1 -0
- data/lib/meta_search/helpers.rb +3 -0
- data/lib/meta_search/helpers/form_builder.rb +152 -0
- data/lib/meta_search/helpers/form_helper.rb +20 -0
- data/lib/meta_search/helpers/url_helper.rb +39 -0
- data/lib/meta_search/method.rb +129 -0
- data/lib/meta_search/model_compatibility.rb +36 -2
- data/lib/meta_search/searches/active_record.rb +88 -11
- data/lib/meta_search/utility.rb +1 -1
- data/lib/meta_search/where.rb +119 -61
- data/meta_search.gemspec +33 -13
- data/test/fixtures/company.rb +15 -2
- data/test/fixtures/data_types.yml +3 -3
- data/test/fixtures/developer.rb +3 -0
- data/test/helper.rb +10 -6
- data/test/test_search.rb +502 -288
- data/test/test_view_helpers.rb +152 -53
- metadata +82 -15
- data/lib/meta_search/helpers/action_view.rb +0 -168
- data/lib/meta_search/railtie.rb +0 -21
- data/lib/meta_search/searches/base.rb +0 -46
data/lib/meta_search/utility.rb
CHANGED
data/lib/meta_search/where.rb
CHANGED
@@ -12,14 +12,14 @@ module MetaSearch
|
|
12
12
|
# === All data types
|
13
13
|
#
|
14
14
|
# * _equals_ (alias: _eq_) - Just as it sounds.
|
15
|
-
# * _does_not_equal_ (
|
15
|
+
# * _does_not_equal_ (aliases: _ne_, _noteq_) - The opposite of equals, oddly enough.
|
16
16
|
# * _in_ - Takes an array, matches on equality with any of the items in the array.
|
17
|
-
# * _not_in_ (
|
17
|
+
# * _not_in_ (aliases: _ni_, _notin_) - Like above, but negated.
|
18
18
|
#
|
19
19
|
# === Strings
|
20
20
|
#
|
21
|
-
# * _contains_ (
|
22
|
-
# * _does_not_contain_ (
|
21
|
+
# * _contains_ (aliases: _like_, _matches_) - Substring match.
|
22
|
+
# * _does_not_contain_ (aliases: _nlike_, _nmatches_) - Negative substring match.
|
23
23
|
# * _starts_with_ (alias: _sw_) - Match strings beginning with the entered term.
|
24
24
|
# * _does_not_start_with_ (alias: _dnsw_) - The opposite of above.
|
25
25
|
# * _ends_with_ (alias: _ew_) - Match strings ending with the entered term.
|
@@ -28,9 +28,9 @@ module MetaSearch
|
|
28
28
|
# === Numbers, dates, and times
|
29
29
|
#
|
30
30
|
# * _greater_than_ (alias: _gt_) - Greater than.
|
31
|
-
# * _greater_than_or_equal_to_ (
|
31
|
+
# * _greater_than_or_equal_to_ (aliases: _gte_, _gteq_) - Greater than or equal to.
|
32
32
|
# * _less_than_ (alias: _lt_) - Less than.
|
33
|
-
# * _less_than_or_equal_to_ (
|
33
|
+
# * _less_than_or_equal_to_ (aliases: _lte_, _lteq_) - Less than or equal to.
|
34
34
|
#
|
35
35
|
# So, given a model like this...
|
36
36
|
#
|
@@ -43,8 +43,16 @@ module MetaSearch
|
|
43
43
|
# ...you might end up with attributes like <tt>title_contains</tt>,
|
44
44
|
# <tt>comments_title_starts_with</tt>, <tt>moderations_value_less_than</tt>,
|
45
45
|
# <tt>author_name_equals</tt>, and so on.
|
46
|
+
#
|
47
|
+
# Additionally, all of the above predicate types also have an _any and _all version, which
|
48
|
+
# expects an array of the corresponding parameter type, and requires any or all of the
|
49
|
+
# parameters to be a match, respectively. So:
|
50
|
+
#
|
51
|
+
# Article.search :author_name_starts_with_any => ['Jim', 'Bob', 'Fred']
|
52
|
+
#
|
53
|
+
# will match articles authored by Jimmy, Bobby, or Freddy, but not Winifred.
|
46
54
|
class Where
|
47
|
-
attr_reader :name, :aliases, :types, :
|
55
|
+
attr_reader :name, :aliases, :types, :predicate, :formatter, :validator
|
48
56
|
def initialize(where)
|
49
57
|
if [String,Symbol].include?(where.class)
|
50
58
|
where = Where.get(where) or raise ArgumentError("A where could not be instantiated for the argument #{where}")
|
@@ -52,22 +60,33 @@ module MetaSearch
|
|
52
60
|
@name = where[:name]
|
53
61
|
@aliases = where[:aliases]
|
54
62
|
@types = where[:types]
|
55
|
-
@
|
56
|
-
@
|
63
|
+
@predicate = where[:predicate]
|
64
|
+
@validator = where[:validator]
|
57
65
|
@formatter = where[:formatter]
|
58
|
-
@
|
66
|
+
@splat_param = where[:splat_param]
|
67
|
+
end
|
68
|
+
|
69
|
+
def splat_param?
|
70
|
+
!!@splat_param
|
71
|
+
end
|
72
|
+
|
73
|
+
# Format a parameter for searching using the Where's defined formatter.
|
74
|
+
def format_param(param)
|
75
|
+
formatter.call(param)
|
59
76
|
end
|
60
77
|
|
61
|
-
|
62
|
-
|
78
|
+
# Validate the parameter for use in a search using the Where's defined validator.
|
79
|
+
def validate(param)
|
80
|
+
validator.call(param)
|
63
81
|
end
|
64
82
|
|
65
|
-
#
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
83
|
+
# Evaluate the Where for the given relation, attribute, and parameter(s)
|
84
|
+
def eval(relation, attribute, param)
|
85
|
+
if splat_param?
|
86
|
+
relation.where(attribute.send(predicate, *format_param(param)))
|
87
|
+
else
|
88
|
+
relation.where(attribute.send(predicate, format_param(param)))
|
89
|
+
end
|
71
90
|
end
|
72
91
|
|
73
92
|
class << self
|
@@ -75,11 +94,12 @@ module MetaSearch
|
|
75
94
|
# in your application's <tt>config/initializers/meta_search.rb</tt>, place lines
|
76
95
|
# like this:
|
77
96
|
#
|
78
|
-
#
|
79
|
-
#
|
80
|
-
#
|
81
|
-
#
|
82
|
-
#
|
97
|
+
# MetaSearch::Where.add :between, :btw,
|
98
|
+
# :predicate => :in,
|
99
|
+
# :types => [:integer, :float, :decimal, :date, :datetime, :timestamp, :time],
|
100
|
+
# :formatter => Proc.new {|param| Range.new(param.first, param.last)},
|
101
|
+
# :validator => Proc.new {|param|
|
102
|
+
# param.is_a?(Array) && !(param[0].blank? || param[1].blank?)
|
83
103
|
# }
|
84
104
|
#
|
85
105
|
# The first options are all names for the where. Well, the first is a name, the rest
|
@@ -90,20 +110,8 @@ module MetaSearch
|
|
90
110
|
# Which is one of several MetaSearch constants available for type assignment (the others
|
91
111
|
# being +DATES+, +TIIMES+, +STRINGS+, and +NUMBERS+).
|
92
112
|
#
|
93
|
-
# <tt>
|
94
|
-
#
|
95
|
-
#
|
96
|
-
# <tt>substitutions</tt> is the text that comes next. It's normally going to have some
|
97
|
-
# question marks in it (for variable substitution) if it's going to be of much use. The
|
98
|
-
# default is ?. Keep in mind if you use more than one ? MetaSearch will require an array
|
99
|
-
# be passed to the attribute for substitution.
|
100
|
-
#
|
101
|
-
# <tt>keep_arrays</tt> tells MetaSearch that if any arrays are received as parameters, they
|
102
|
-
# should be used as-is in the substitution, rather than flattened and passed as a list.
|
103
|
-
# For example, this is the definition of the "in" Where:
|
104
|
-
#
|
105
|
-
# ['in', {:types => ALL_TYPES, :condition => 'IN', :substitutions => '(?)',
|
106
|
-
# :keep_arrays => true}]
|
113
|
+
# <tt>predicate</tt> is the Arel::Attribute predication (read: conditional operator) used
|
114
|
+
# for the comparison. Default is :eq, or equality.
|
107
115
|
#
|
108
116
|
# <tt>formatter</tt> is the Proc that will do any formatting to the variables to be substituted.
|
109
117
|
# The default proc is <tt>{|param| param}</tt>, which doesn't really do anything. If you pass a
|
@@ -111,25 +119,73 @@ module MetaSearch
|
|
111
119
|
#
|
112
120
|
# For example, this is the definition of the "contains" Where:
|
113
121
|
#
|
114
|
-
# ['contains', 'like', {:types => STRINGS, :
|
122
|
+
# ['contains', 'like', {:types => STRINGS, :predicate => :matches, :formatter => '"%#{param}%"'}]
|
115
123
|
#
|
116
124
|
# Be sure to single-quote the string, so that variables aren't interpolated until later. If in doubt,
|
117
125
|
# just use a Proc.
|
126
|
+
#
|
127
|
+
# <tt>validator</tt> is the Proc that will be used to check whether a parameter supplied to the
|
128
|
+
# Where is valid. If it is not valid, it won't be used in the query. The default is
|
129
|
+
# <tt>{|param| !param.blank?}</tt>, so that empty parameters aren't added to the search, but you
|
130
|
+
# can get more complex if you desire, like the one in the between example, above.
|
131
|
+
#
|
132
|
+
# <tt>splat_param</tt>, if true, will cause the parameters sent to the predicate in question
|
133
|
+
# to be splatted (converted to an argument list). This is not normally useful and defaults to
|
134
|
+
# false, but is used when automatically creating compound Wheres (*_any, *_all) so that the
|
135
|
+
# Arel attribute method gets the correct parameter list.
|
118
136
|
def add(*args)
|
137
|
+
where = create_where_from_args(*args)
|
138
|
+
create_where_compounds_for(where)
|
139
|
+
end
|
140
|
+
|
141
|
+
# Returns the complete array of Wheres
|
142
|
+
def all
|
143
|
+
@@wheres
|
144
|
+
end
|
145
|
+
|
146
|
+
# Get the where matching a method or predicate.
|
147
|
+
def get(method_id_or_predicate)
|
148
|
+
return nil unless where_key = @@wheres.keys.
|
149
|
+
sort {|a,b| b.length <=> a.length}.
|
150
|
+
detect {|n| method_id_or_predicate.to_s.match(/#{n}=?$/)}
|
151
|
+
where = @@wheres[where_key]
|
152
|
+
where = @@wheres[where] if where.is_a?(String)
|
153
|
+
where
|
154
|
+
end
|
155
|
+
|
156
|
+
# Set the wheres to their default values, removing any customized settings.
|
157
|
+
def initialize_wheres
|
158
|
+
@@wheres = {}
|
159
|
+
DEFAULT_WHERES.each do |where|
|
160
|
+
add(*where)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
private
|
165
|
+
|
166
|
+
# "Creates" the Where by adding it (and its aliases) to the current hash of wheres. It then
|
167
|
+
# instantiates a Where and returns it for use.
|
168
|
+
def create_where_from_args(*args)
|
119
169
|
opts = args.last.is_a?(Hash) ? args.pop : {}
|
120
170
|
args = args.compact.flatten.map {|a| a.to_s }
|
121
171
|
raise ArgumentError, "Name parameter required" if args.blank?
|
122
172
|
opts[:name] ||= args.first
|
123
173
|
opts[:types] ||= ALL_TYPES
|
124
174
|
opts[:types] = [opts[:types]].flatten
|
125
|
-
opts[:
|
126
|
-
opts[:
|
127
|
-
opts[:keep_arrays] ||= false
|
175
|
+
opts[:predicate] ||= :eq
|
176
|
+
opts[:splat_param] ||= false
|
128
177
|
opts[:formatter] ||= Proc.new {|param| param}
|
129
178
|
if opts[:formatter].is_a?(String)
|
130
179
|
formatter = opts[:formatter]
|
131
180
|
opts[:formatter] = Proc.new {|param| eval formatter}
|
132
181
|
end
|
182
|
+
unless opts[:formatter].respond_to?(:call)
|
183
|
+
raise ArgumentError, "Invalid formatter for #{opts[:name]}, should be a Proc or String."
|
184
|
+
end
|
185
|
+
opts[:validator] ||= Proc.new {|param| !param.blank?}
|
186
|
+
unless opts[:validator].respond_to?(:call)
|
187
|
+
raise ArgumentError, "Invalid validator for #{opts[:name]}, should be a Proc."
|
188
|
+
end
|
133
189
|
opts[:aliases] ||= [args - [opts[:name]]].flatten
|
134
190
|
@@wheres ||= {}
|
135
191
|
if @@wheres.has_key?(opts[:name])
|
@@ -143,28 +199,30 @@ module MetaSearch
|
|
143
199
|
@@wheres[a] = opts[:name]
|
144
200
|
end
|
145
201
|
end
|
202
|
+
new(opts[:name])
|
146
203
|
end
|
147
204
|
|
148
|
-
#
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
205
|
+
# Takes the provided +where+ param and derives two additional Wheres from it, with the
|
206
|
+
# name appended by _any/_all. These will use Arel's grouped predicate methods (matching
|
207
|
+
# the same naming convention) to be invoked instead, with a list of possible/required
|
208
|
+
# matches.
|
209
|
+
def create_where_compounds_for(where)
|
210
|
+
['any', 'all'].each do |compound|
|
211
|
+
args = [where.name, *where.aliases].map {|n| "#{n}_#{compound}"}
|
212
|
+
create_where_from_args(*args + [{
|
213
|
+
:types => where.types,
|
214
|
+
:predicate => "#{where.predicate}_#{compound}".to_sym,
|
215
|
+
:splat_param => true,
|
216
|
+
# Only use valid elements in the array
|
217
|
+
:formatter => Proc.new {|param|
|
218
|
+
param.select {|p| where.validator.call(p)}.map {|p| where.formatter.call(p)}
|
219
|
+
},
|
220
|
+
# Compound where is valid if it has at least one element which is valid
|
221
|
+
:validator => Proc.new {|param|
|
222
|
+
param.is_a?(Array) &&
|
223
|
+
!param.select {|p| where.validator.call(p)}.blank?}
|
224
|
+
}]
|
225
|
+
)
|
168
226
|
end
|
169
227
|
end
|
170
228
|
end
|
data/meta_search.gemspec
CHANGED
@@ -5,12 +5,16 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{meta_search}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.5.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Ernie Miller"]
|
12
|
-
s.date = %q{2010-
|
13
|
-
s.description = %q{
|
12
|
+
s.date = %q{2010-06-08}
|
13
|
+
s.description = %q{
|
14
|
+
Allows simple search forms to be created against an AR3 model
|
15
|
+
and its associations, has useful view helpers for sort links
|
16
|
+
and multiparameter fields as well.
|
17
|
+
}
|
14
18
|
s.email = %q{ernie@metautonomo.us}
|
15
19
|
s.extra_rdoc_files = [
|
16
20
|
"LICENSE",
|
@@ -19,6 +23,8 @@ Gem::Specification.new do |s|
|
|
19
23
|
s.files = [
|
20
24
|
".document",
|
21
25
|
".gitignore",
|
26
|
+
".gitmodules",
|
27
|
+
"Gemfile",
|
22
28
|
"LICENSE",
|
23
29
|
"README.rdoc",
|
24
30
|
"Rakefile",
|
@@ -26,11 +32,13 @@ Gem::Specification.new do |s|
|
|
26
32
|
"lib/meta_search.rb",
|
27
33
|
"lib/meta_search/builder.rb",
|
28
34
|
"lib/meta_search/exceptions.rb",
|
29
|
-
"lib/meta_search/helpers
|
35
|
+
"lib/meta_search/helpers.rb",
|
36
|
+
"lib/meta_search/helpers/form_builder.rb",
|
37
|
+
"lib/meta_search/helpers/form_helper.rb",
|
38
|
+
"lib/meta_search/helpers/url_helper.rb",
|
39
|
+
"lib/meta_search/method.rb",
|
30
40
|
"lib/meta_search/model_compatibility.rb",
|
31
|
-
"lib/meta_search/railtie.rb",
|
32
41
|
"lib/meta_search/searches/active_record.rb",
|
33
|
-
"lib/meta_search/searches/base.rb",
|
34
42
|
"lib/meta_search/utility.rb",
|
35
43
|
"lib/meta_search/where.rb",
|
36
44
|
"meta_search.gemspec",
|
@@ -50,11 +58,11 @@ Gem::Specification.new do |s|
|
|
50
58
|
"test/test_search.rb",
|
51
59
|
"test/test_view_helpers.rb"
|
52
60
|
]
|
53
|
-
s.homepage = %q{http://metautonomo.us}
|
61
|
+
s.homepage = %q{http://metautonomo.us/projects/metasearch/}
|
54
62
|
s.rdoc_options = ["--charset=UTF-8"]
|
55
63
|
s.require_paths = ["lib"]
|
56
|
-
s.rubygems_version = %q{1.3.
|
57
|
-
s.summary = %q{ActiveRecord 3 object-based searching.}
|
64
|
+
s.rubygems_version = %q{1.3.7}
|
65
|
+
s.summary = %q{ActiveRecord 3 object-based searching for your form_for enjoyment.}
|
58
66
|
s.test_files = [
|
59
67
|
"test/fixtures/company.rb",
|
60
68
|
"test/fixtures/data_type.rb",
|
@@ -71,13 +79,25 @@ Gem::Specification.new do |s|
|
|
71
79
|
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
72
80
|
s.specification_version = 3
|
73
81
|
|
74
|
-
if Gem::Version.new(Gem::
|
75
|
-
s.add_development_dependency(%q<
|
82
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
83
|
+
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
84
|
+
s.add_runtime_dependency(%q<activerecord>, [">= 3.0.0.beta4"])
|
85
|
+
s.add_runtime_dependency(%q<activesupport>, [">= 3.0.0.beta4"])
|
86
|
+
s.add_runtime_dependency(%q<actionpack>, [">= 3.0.0.beta4"])
|
87
|
+
s.add_runtime_dependency(%q<arel>, [">= 0.4.0"])
|
76
88
|
else
|
77
|
-
s.add_dependency(%q<
|
89
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
90
|
+
s.add_dependency(%q<activerecord>, [">= 3.0.0.beta4"])
|
91
|
+
s.add_dependency(%q<activesupport>, [">= 3.0.0.beta4"])
|
92
|
+
s.add_dependency(%q<actionpack>, [">= 3.0.0.beta4"])
|
93
|
+
s.add_dependency(%q<arel>, [">= 0.4.0"])
|
78
94
|
end
|
79
95
|
else
|
80
|
-
s.add_dependency(%q<
|
96
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
97
|
+
s.add_dependency(%q<activerecord>, [">= 3.0.0.beta4"])
|
98
|
+
s.add_dependency(%q<activesupport>, [">= 3.0.0.beta4"])
|
99
|
+
s.add_dependency(%q<actionpack>, [">= 3.0.0.beta4"])
|
100
|
+
s.add_dependency(%q<arel>, [">= 0.4.0"])
|
81
101
|
end
|
82
102
|
end
|
83
103
|
|
data/test/fixtures/company.rb
CHANGED
@@ -4,6 +4,19 @@ class Company < ActiveRecord::Base
|
|
4
4
|
has_many :slackers, :class_name => "Developer", :conditions => {:slacker => true}
|
5
5
|
has_many :notes, :as => :notable
|
6
6
|
has_many :data_types
|
7
|
-
|
8
|
-
|
7
|
+
|
8
|
+
scope :backwards_name, lambda {|name| where(:name => name.reverse)}
|
9
|
+
scope :with_slackers_by_name_and_salary_range,
|
10
|
+
lambda {|name, low, high|
|
11
|
+
joins(:slackers).where(:developers => {:name => name, :salary => low..high})
|
12
|
+
}
|
13
|
+
search_methods :backwards_name, :backwards_name_as_string
|
14
|
+
search_methods :with_slackers_by_name_and_salary_range,
|
15
|
+
:splat_param => true, :type => [:string, :integer, :integer]
|
16
|
+
attr_unsearchable :updated_at
|
17
|
+
assoc_unsearchable :notes
|
18
|
+
|
19
|
+
def self.backwards_name_as_string(name)
|
20
|
+
name.reverse
|
21
|
+
end
|
9
22
|
end
|
@@ -6,9 +6,9 @@ dt_<%= n %>:
|
|
6
6
|
int : <%= n ** 3 %>
|
7
7
|
flt : <%= n.to_f / 2.0 %>
|
8
8
|
dec : <%= n.to_f ** (n + 0.1) %>
|
9
|
-
dtm : <%= Time.utc(2009, 12, 24) + 86400 * n %>
|
10
|
-
tms : <%= Time.utc(2009, 12, 24) + 86400 * n %>
|
11
|
-
tim : <%= Time.utc(2000, 01, 01, n+8, n) %>
|
9
|
+
dtm : <%= (Time.utc(2009, 12, 24) + 86400 * n).to_s(:db) %>
|
10
|
+
tms : <%= (Time.utc(2009, 12, 24) + 86400 * n).to_s(:db) %>
|
11
|
+
tim : <%= Time.utc(2000, 01, 01, n+8, n).to_s(:db) %>
|
12
12
|
dat : <%= (Date.new(2009, 12, 24) + n).strftime("%Y-%m-%d") %>
|
13
13
|
bin : <%= "BLOB#{n}" * n %>
|
14
14
|
bln : <%= n % 2 > 0 ? true : false %>
|
data/test/fixtures/developer.rb
CHANGED
data/test/helper.rb
CHANGED
@@ -10,7 +10,7 @@ FIXTURES_PATH = File.join(File.dirname(__FILE__), 'fixtures')
|
|
10
10
|
Time.zone = 'Eastern Time (US & Canada)'
|
11
11
|
|
12
12
|
ActiveRecord::Base.establish_connection(
|
13
|
-
:adapter => 'sqlite3',
|
13
|
+
:adapter => defined?(JRUBY_VERSION) ? 'jdbcsqlite3' : 'sqlite3',
|
14
14
|
:database => ':memory:'
|
15
15
|
)
|
16
16
|
|
@@ -27,11 +27,15 @@ Fixtures.create_fixtures(FIXTURES_PATH, ActiveRecord::Base.connection.tables)
|
|
27
27
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
28
28
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
29
29
|
require 'meta_search'
|
30
|
-
require 'meta_search/searches/active_record'
|
31
|
-
require 'meta_search/helpers/action_view'
|
32
|
-
|
33
|
-
MetaSearch::Searches::ActiveRecord.enable!
|
34
|
-
MetaSearch::Helpers::FormBuilder.enable!
|
35
30
|
|
36
31
|
class Test::Unit::TestCase
|
32
|
+
def self.context_a_search_against(name, object, &block)
|
33
|
+
context "A search against #{name}" do
|
34
|
+
setup do
|
35
|
+
@s = object.search
|
36
|
+
end
|
37
|
+
|
38
|
+
merge_block(&block) if block_given?
|
39
|
+
end
|
40
|
+
end
|
37
41
|
end
|
data/test/test_search.rb
CHANGED
@@ -1,350 +1,564 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
3
|
class TestSearch < Test::Unit::TestCase
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
should "have an association named developers" do
|
11
|
-
assert @s.association(:developers)
|
12
|
-
end
|
13
|
-
|
14
|
-
should "have a column named name" do
|
15
|
-
assert @s.column(:name)
|
16
|
-
end
|
17
|
-
|
18
|
-
should "exclude the column named updated_at" do
|
19
|
-
assert_nil @s.column(:updated_at)
|
20
|
-
end
|
21
|
-
|
22
|
-
should "raise an error if we try to search on updated_at" do
|
23
|
-
assert_raise NoMethodError do
|
24
|
-
@s.updated_at_eq = [2009, 1, 1]
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
should "exclude the association named notes" do
|
29
|
-
assert_nil @s.association(:notes)
|
30
|
-
end
|
31
|
-
|
32
|
-
should "raise an error if we try to search on notes" do
|
33
|
-
assert_raise NoMethodError do
|
34
|
-
@s.notes_note_eq = 'Blah'
|
4
|
+
|
5
|
+
[{:name => 'Company', :object => Company},
|
6
|
+
{:name => 'Company as a Relation', :object => Company.scoped}].each do |object|
|
7
|
+
context_a_search_against object[:name], object[:object] do
|
8
|
+
should "have a relation attribute which is an ActiveRecord::Relation" do
|
9
|
+
assert_equal ActiveRecord::Relation, @s.relation.class
|
35
10
|
end
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
end
|
41
|
-
|
42
|
-
should "raise an error if we try to search data_types.str" do
|
43
|
-
assert_raise NoMethodError do
|
44
|
-
@s.data_types_str_eq = 'Blah'
|
11
|
+
|
12
|
+
should "have a base attribute which is a Class inheriting from ActiveRecord::Base" do
|
13
|
+
assert_equal Company, @s.base
|
14
|
+
assert_contains @s.base.ancestors, ActiveRecord::Base
|
45
15
|
end
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
setup do
|
50
|
-
@s.name_contains = 'optical'
|
16
|
+
|
17
|
+
should "have an association named developers" do
|
18
|
+
assert @s.get_association(:developers)
|
51
19
|
end
|
52
|
-
|
53
|
-
should "
|
54
|
-
|
20
|
+
|
21
|
+
should "have a column named name" do
|
22
|
+
assert @s.get_column(:name)
|
55
23
|
end
|
56
|
-
|
57
|
-
should "
|
58
|
-
|
24
|
+
|
25
|
+
should "exclude the column named updated_at" do
|
26
|
+
assert_nil @s.get_column(:updated_at)
|
59
27
|
end
|
60
|
-
|
61
|
-
should "
|
62
|
-
|
28
|
+
|
29
|
+
should "raise an error if we try to search on updated_at" do
|
30
|
+
assert_raise NoMethodError do
|
31
|
+
@s.updated_at_eq = [2009, 1, 1]
|
32
|
+
end
|
63
33
|
end
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
setup do
|
68
|
-
@s.developers_name_starts_with = 'Ernie'
|
34
|
+
|
35
|
+
should "exclude the association named notes" do
|
36
|
+
assert_nil @s.get_association(:notes)
|
69
37
|
end
|
70
|
-
|
71
|
-
should "
|
72
|
-
|
38
|
+
|
39
|
+
should "raise an error if we try to search on notes" do
|
40
|
+
assert_raise NoMethodError do
|
41
|
+
@s.notes_note_eq = 'Blah'
|
42
|
+
end
|
73
43
|
end
|
74
|
-
|
75
|
-
should "
|
76
|
-
|
44
|
+
|
45
|
+
should "honor its associations' excluded attributes" do
|
46
|
+
assert_nil @s.get_attribute(:data_types_str)
|
77
47
|
end
|
78
|
-
|
79
|
-
should "
|
80
|
-
|
48
|
+
|
49
|
+
should "raise an error if we try to search data_types.str" do
|
50
|
+
assert_raise NoMethodError do
|
51
|
+
@s.data_types_str_eq = 'Blah'
|
52
|
+
end
|
81
53
|
end
|
82
|
-
|
83
|
-
context "
|
54
|
+
|
55
|
+
context "sorted by name in ascending order" do
|
84
56
|
setup do
|
85
|
-
@s.
|
57
|
+
@s.meta_sort = 'name.asc'
|
86
58
|
end
|
87
|
-
|
88
|
-
should "
|
89
|
-
assert_equal
|
59
|
+
|
60
|
+
should "sort by name in ascending order" do
|
61
|
+
assert_equal Company.order('name asc').all,
|
62
|
+
@s.all
|
90
63
|
end
|
91
|
-
|
92
|
-
|
93
|
-
|
64
|
+
end
|
65
|
+
|
66
|
+
context "sorted by name in descending order" do
|
67
|
+
setup do
|
68
|
+
@s.meta_sort = 'name.desc'
|
94
69
|
end
|
95
|
-
|
96
|
-
should "
|
97
|
-
|
70
|
+
|
71
|
+
should "sort by name in descending order" do
|
72
|
+
assert_equal Company.order('name desc').all,
|
73
|
+
@s.all
|
98
74
|
end
|
99
75
|
end
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
context "A developer search" do
|
122
|
-
setup do
|
123
|
-
@s = Developer.search
|
124
|
-
end
|
125
|
-
|
126
|
-
should "have an association named projects" do
|
127
|
-
assert @s.association(:projects)
|
128
|
-
end
|
129
|
-
|
130
|
-
context "where developer is Bob-approved" do
|
131
|
-
setup do
|
132
|
-
@s.notes_note_equals = "A straight shooter with upper management written all over him."
|
133
|
-
end
|
134
|
-
|
135
|
-
should "return Peter Gibbons" do
|
136
|
-
assert_contains @s.all, Developer.where(:name => 'Peter Gibbons').first
|
76
|
+
|
77
|
+
context "where name contains optical" do
|
78
|
+
setup do
|
79
|
+
@s.name_contains = 'optical'
|
80
|
+
end
|
81
|
+
|
82
|
+
should "return one result" do
|
83
|
+
assert_equal 1, @s.all.size
|
84
|
+
end
|
85
|
+
|
86
|
+
should "return a company named Advanced Optical Solutions" do
|
87
|
+
assert_contains @s.all, Company.where(:name => 'Advanced Optical Solutions').first
|
88
|
+
end
|
89
|
+
|
90
|
+
should "not return a company named Initech" do
|
91
|
+
assert_does_not_contain @s.all, Company.where(:name => "Initech").first
|
92
|
+
end
|
137
93
|
end
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
94
|
+
|
95
|
+
context "where developer name starts with Ernie" do
|
96
|
+
setup do
|
97
|
+
@s.developers_name_starts_with = 'Ernie'
|
98
|
+
end
|
99
|
+
|
100
|
+
should "return one result" do
|
101
|
+
assert_equal 1, @s.all.size
|
102
|
+
end
|
103
|
+
|
104
|
+
should "return a company named Mission Data" do
|
105
|
+
assert_contains @s.all, Company.where(:name => 'Mission Data').first
|
106
|
+
end
|
107
|
+
|
108
|
+
should "not return a company named Initech" do
|
109
|
+
assert_does_not_contain @s.all, Company.where(:name => "Initech").first
|
110
|
+
end
|
111
|
+
|
112
|
+
context "and slackers salary is greater than $70k" do
|
113
|
+
setup do
|
114
|
+
@s.slackers_salary_gt = 70000
|
115
|
+
end
|
116
|
+
|
117
|
+
should "return no results" do
|
118
|
+
assert_equal 0, @s.all.size
|
119
|
+
end
|
120
|
+
|
121
|
+
should "join developers twice" do
|
122
|
+
assert @s.to_sql.match(/join\s+"?developers"?.*join\s+"?developers"?/i)
|
123
|
+
end
|
124
|
+
|
125
|
+
should "alias the second join of developers" do
|
126
|
+
assert @s.to_sql.match(/join\s+"?developers"?\s+"?slackers_companies"?/i)
|
127
|
+
end
|
128
|
+
end
|
143
129
|
end
|
144
|
-
|
145
|
-
|
146
|
-
|
130
|
+
|
131
|
+
context "where developer note indicates he will crack yo skull" do
|
132
|
+
setup do
|
133
|
+
@s.developer_notes_note_equals = "Will show you what he's doing."
|
134
|
+
end
|
135
|
+
|
136
|
+
should "return one result" do
|
137
|
+
assert_equal 1, @s.all.size
|
138
|
+
end
|
139
|
+
|
140
|
+
should "return a company named Advanced Optical Solutions" do
|
141
|
+
assert_contains @s.all, Company.where(:name => 'Advanced Optical Solutions').first
|
142
|
+
end
|
143
|
+
|
144
|
+
should "not return a company named Mission Data" do
|
145
|
+
assert_does_not_contain @s.all, Company.where(:name => "Mission Data").first
|
146
|
+
end
|
147
147
|
end
|
148
|
-
|
149
|
-
|
150
|
-
|
148
|
+
|
149
|
+
context "where developer note indicates he will crack yo skull through two associations" do
|
150
|
+
setup do
|
151
|
+
@s.developers_notes_note_equals = "Will show you what he's doing."
|
152
|
+
end
|
153
|
+
|
154
|
+
should "return one result" do
|
155
|
+
assert_equal 1, @s.all.size
|
156
|
+
end
|
157
|
+
|
158
|
+
should "return a company named Advanced Optical Solutions" do
|
159
|
+
assert_contains @s.all, Company.where(:name => 'Advanced Optical Solutions').first
|
160
|
+
end
|
161
|
+
|
162
|
+
should "not return a company named Mission Data" do
|
163
|
+
assert_does_not_contain @s.all, Company.where(:name => "Mission Data").first
|
164
|
+
end
|
151
165
|
end
|
152
|
-
|
153
|
-
|
154
|
-
|
166
|
+
|
167
|
+
context "where developer note indicates he will crack yo skull through four associations" do
|
168
|
+
setup do
|
169
|
+
@s.developers_company_developers_notes_note_equals = "Will show you what he's doing."
|
170
|
+
end
|
171
|
+
|
172
|
+
should "return two results, one of which is a duplicate due to joins" do
|
173
|
+
assert_equal 2, @s.all.size
|
174
|
+
assert_equal 1, @s.all.uniq.size
|
175
|
+
end
|
176
|
+
|
177
|
+
should "return a company named Advanced Optical Solutions" do
|
178
|
+
assert_contains @s.all, Company.where(:name => 'Advanced Optical Solutions').first
|
179
|
+
end
|
180
|
+
|
181
|
+
should "not return a company named Mission Data" do
|
182
|
+
assert_does_not_contain @s.all, Company.where(:name => "Mission Data").first
|
183
|
+
end
|
155
184
|
end
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
185
|
+
|
186
|
+
context "with a join more than five tables deep (including source table)" do
|
187
|
+
setup do
|
188
|
+
@s.developers_company_developers_company_developers_name_equals = "Ernie Miller"
|
189
|
+
end
|
190
|
+
|
191
|
+
should "raise an error when the relation is accessed" do
|
192
|
+
assert_raise MetaSearch::JoinDepthError do
|
193
|
+
@s.all
|
194
|
+
end
|
195
|
+
end
|
161
196
|
end
|
162
|
-
|
163
|
-
|
164
|
-
|
197
|
+
|
198
|
+
context "where backwards name is hcetinI" do
|
199
|
+
setup do
|
200
|
+
@s.backwards_name = 'hcetinI'
|
201
|
+
end
|
202
|
+
|
203
|
+
should "return 1 result" do
|
204
|
+
assert_equal 1, @s.all.size
|
205
|
+
end
|
206
|
+
|
207
|
+
should "return a company named Initech" do
|
208
|
+
assert_contains @s.all, Company.where(:name => 'Initech').first
|
209
|
+
end
|
165
210
|
end
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
211
|
+
|
212
|
+
context "where with_slackers_by_name_and_salary_range is sent an array with 3 values" do
|
213
|
+
setup do
|
214
|
+
@s.with_slackers_by_name_and_salary_range = ['Peter Gibbons', 90000, 110000]
|
215
|
+
end
|
216
|
+
|
217
|
+
should "return 1 result" do
|
218
|
+
assert_equal 1, @s.all.size
|
219
|
+
end
|
220
|
+
|
221
|
+
should "return a company named Initech" do
|
222
|
+
assert_contains @s.all, Company.where(:name => 'Initech').first
|
223
|
+
end
|
170
224
|
end
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
225
|
+
|
226
|
+
should "raise an error when the wrong number of parameters would be supplied to a custom search" do
|
227
|
+
assert_raise ArgumentError do
|
228
|
+
@s.with_slackers_by_name_and_salary_range = ['Peter Gibbons', 90000]
|
229
|
+
end
|
176
230
|
end
|
177
|
-
|
178
|
-
should "return
|
179
|
-
|
231
|
+
|
232
|
+
should "raise an error when a custom search method does not return a relation" do
|
233
|
+
assert_raise MetaSearch::NonRelationReturnedError do
|
234
|
+
@s.backwards_name_as_string = 'hcetinI'
|
235
|
+
end
|
180
236
|
end
|
181
237
|
end
|
182
238
|
end
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
should "raise an error on a contains search against a boolean column" do
|
190
|
-
assert_raise NoMethodError do
|
191
|
-
@s.bln_contains = "true"
|
192
|
-
end
|
193
|
-
end
|
194
|
-
|
195
|
-
context "where boolean column equals true" do
|
196
|
-
setup do
|
197
|
-
@s.bln_equals = true
|
198
|
-
end
|
199
|
-
|
200
|
-
should "return five results" do
|
201
|
-
assert_equal 5, @s.all.size
|
239
|
+
|
240
|
+
[{:name => 'Developer', :object => Developer},
|
241
|
+
{:name => 'Developer as a Relation', :object => Developer.scoped}].each do |object|
|
242
|
+
context_a_search_against object[:name], object[:object] do
|
243
|
+
should "exclude the column named company_id" do
|
244
|
+
assert_nil @s.get_column(:company_id)
|
202
245
|
end
|
203
|
-
|
204
|
-
should "
|
205
|
-
|
246
|
+
|
247
|
+
should "have an association named projects" do
|
248
|
+
assert @s.get_association(:projects)
|
206
249
|
end
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
250
|
+
|
251
|
+
context "sorted by company name in ascending order" do
|
252
|
+
setup do
|
253
|
+
@s.meta_sort = 'company_name.asc'
|
254
|
+
end
|
255
|
+
|
256
|
+
should "sort by company name in ascending order" do
|
257
|
+
assert_equal Developer.joins(:company).order('companies.name asc').all,
|
258
|
+
@s.all
|
259
|
+
end
|
212
260
|
end
|
213
|
-
|
214
|
-
|
215
|
-
|
261
|
+
|
262
|
+
context "sorted by company name in descending order" do
|
263
|
+
setup do
|
264
|
+
@s.meta_sort = 'company_name.desc'
|
265
|
+
end
|
266
|
+
|
267
|
+
should "sort by company name in descending order" do
|
268
|
+
assert_equal Developer.joins(:company).order('companies.name desc').all,
|
269
|
+
@s.all
|
270
|
+
end
|
216
271
|
end
|
217
|
-
|
218
|
-
|
219
|
-
|
272
|
+
|
273
|
+
context "where developer is Bob-approved" do
|
274
|
+
setup do
|
275
|
+
@s.notes_note_equals = "A straight shooter with upper management written all over him."
|
276
|
+
end
|
277
|
+
|
278
|
+
should "return Peter Gibbons" do
|
279
|
+
assert_contains @s.all, Developer.where(:name => 'Peter Gibbons').first
|
280
|
+
end
|
220
281
|
end
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
282
|
+
|
283
|
+
context "where name ends with Miller" do
|
284
|
+
setup do
|
285
|
+
@s.name_ends_with = 'Miller'
|
286
|
+
end
|
287
|
+
|
288
|
+
should "return one result" do
|
289
|
+
assert_equal 1, @s.all.size
|
290
|
+
end
|
291
|
+
|
292
|
+
should "return a developer named Ernie Miller" do
|
293
|
+
assert_contains @s.all, Developer.where(:name => 'Ernie Miller').first
|
294
|
+
end
|
295
|
+
|
296
|
+
should "not return a developer named Herb Myers" do
|
297
|
+
assert_does_not_contain @s.all, Developer.where(:name => "Herb Myers").first
|
298
|
+
end
|
226
299
|
end
|
227
|
-
|
228
|
-
|
229
|
-
|
300
|
+
|
301
|
+
context "where name starts with any of Ernie, Herb, or Peter" do
|
302
|
+
setup do
|
303
|
+
@s.name_starts_with_any = ['Ernie', 'Herb', 'Peter']
|
304
|
+
end
|
305
|
+
|
306
|
+
should "return three results" do
|
307
|
+
assert_equal 3, @s.all.size
|
308
|
+
end
|
309
|
+
|
310
|
+
should "return a developer named Ernie Miller" do
|
311
|
+
assert_contains @s.all, Developer.where(:name => 'Ernie Miller').first
|
312
|
+
end
|
313
|
+
|
314
|
+
should "not return a developer named Samir Nagheenanajar" do
|
315
|
+
assert_does_not_contain @s.all, Developer.where(:name => "Samir Nagheenanajar").first
|
316
|
+
end
|
230
317
|
end
|
231
|
-
|
232
|
-
|
233
|
-
|
318
|
+
|
319
|
+
context "where name does not equal Ernie Miller" do
|
320
|
+
setup do
|
321
|
+
@s.name_ne = 'Ernie Miller'
|
322
|
+
end
|
323
|
+
|
324
|
+
should "return seven results" do
|
325
|
+
assert_equal 7, @s.all.size
|
326
|
+
end
|
327
|
+
|
328
|
+
should "not return a developer named Ernie Miller" do
|
329
|
+
assert_does_not_contain @s.all, Developer.where(:name => "Ernie Miller").first
|
330
|
+
end
|
234
331
|
end
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
332
|
+
|
333
|
+
context "where name contains all of a, e, and i" do
|
334
|
+
setup do
|
335
|
+
@s.name_contains_all = ['a', 'e', 'i']
|
336
|
+
end
|
337
|
+
|
338
|
+
should "return two results" do
|
339
|
+
assert_equal 2, @s.all.size
|
340
|
+
end
|
341
|
+
|
342
|
+
should "return a developer named Samir Nagheenanajar" do
|
343
|
+
assert_contains @s.all, Developer.where(:name => "Samir Nagheenanajar").first
|
344
|
+
end
|
345
|
+
|
346
|
+
should "not return a developer named Ernie Miller" do
|
347
|
+
assert_does_not_contain @s.all, Developer.where(:name => 'Ernie Miller').first
|
348
|
+
end
|
241
349
|
end
|
242
|
-
|
243
|
-
|
244
|
-
|
350
|
+
|
351
|
+
context "where project estimated hours are greater than or equal to 1000" do
|
352
|
+
setup do
|
353
|
+
@s.projects_estimated_hours_gte = 1000
|
354
|
+
end
|
355
|
+
|
356
|
+
should "return three results" do
|
357
|
+
assert_equal 3, @s.all.size
|
358
|
+
end
|
359
|
+
|
360
|
+
should "return these developers" do
|
361
|
+
assert_same_elements @s.all.collect {|d| d.name},
|
362
|
+
['Peter Gibbons', 'Michael Bolton', 'Samir Nagheenanajar']
|
363
|
+
end
|
245
364
|
end
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
365
|
+
|
366
|
+
context "where project estimated hours are greater than 1000" do
|
367
|
+
setup do
|
368
|
+
@s.projects_estimated_hours_gt = 1000
|
369
|
+
end
|
370
|
+
|
371
|
+
should "return no results" do
|
372
|
+
assert_equal 0, @s.all.size
|
373
|
+
end
|
251
374
|
end
|
252
375
|
end
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
376
|
+
end
|
377
|
+
|
378
|
+
[{:name => 'DataType', :object => DataType},
|
379
|
+
{:name => 'DataType as a Relation', :object => DataType.scoped}].each do |object|
|
380
|
+
context_a_search_against object[:name], object[:object] do
|
381
|
+
should "raise an error on a contains search against a boolean column" do
|
382
|
+
assert_raise NoMethodError do
|
383
|
+
@s.bln_contains = "true"
|
384
|
+
end
|
261
385
|
end
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
386
|
+
|
387
|
+
context "where boolean column equals true" do
|
388
|
+
setup do
|
389
|
+
@s.bln_equals = true
|
390
|
+
end
|
391
|
+
|
392
|
+
should "return five results" do
|
393
|
+
assert_equal 5, @s.all.size
|
394
|
+
end
|
395
|
+
|
396
|
+
should "contain no results with a false boolean column" do
|
397
|
+
assert_does_not_contain @s.all.collect {|r| r.bln}, false
|
398
|
+
end
|
267
399
|
end
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
400
|
+
|
401
|
+
context "where date column is Christmas 2009 by array" do
|
402
|
+
setup do
|
403
|
+
@s.dat_equals = [2009, 12, 25]
|
404
|
+
end
|
405
|
+
|
406
|
+
should "return one result" do
|
407
|
+
assert_equal 1, @s.all.size
|
408
|
+
end
|
409
|
+
|
410
|
+
should "contain a result with Christmas 2009 as its date" do
|
411
|
+
assert_equal Date.parse('2009/12/25'), @s.first.dat
|
412
|
+
end
|
273
413
|
end
|
274
|
-
|
275
|
-
|
276
|
-
|
414
|
+
|
415
|
+
context "where date column is Christmas 2009 by Date object" do
|
416
|
+
setup do
|
417
|
+
@s.dat_equals = Date.new(2009, 12, 25)
|
418
|
+
end
|
419
|
+
|
420
|
+
should "return one result" do
|
421
|
+
assert_equal 1, @s.all.size
|
422
|
+
end
|
423
|
+
|
424
|
+
should "contain a result with Christmas 2009 as its date" do
|
425
|
+
assert_equal Date.parse('2009/12/25'), @s.first.dat
|
426
|
+
end
|
277
427
|
end
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
428
|
+
|
429
|
+
context "where time column is > 1:00 PM and < 3:30 PM" do
|
430
|
+
setup do
|
431
|
+
@s.tim_gt = Time.parse('2000-01-01 13:00') # Rails "dummy time" format
|
432
|
+
@s.tim_lt = Time.parse('2000-01-01 15:30') # Rails "dummy time" format
|
433
|
+
end
|
434
|
+
|
435
|
+
should "return three results" do
|
436
|
+
assert_equal 3, @s.all.size
|
437
|
+
end
|
438
|
+
|
439
|
+
should "not contain results with time column before or after constraints" do
|
440
|
+
assert_equal [], @s.all.select {|r|
|
441
|
+
r.tim < Time.parse('2000-01-01 13:00') || r.tim > Time.parse('2000-01-01 15:30')
|
442
|
+
}
|
443
|
+
end
|
283
444
|
end
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
445
|
+
|
446
|
+
context "where timestamp column is in the year 2010" do
|
447
|
+
setup do
|
448
|
+
@s.tms_gte = Time.utc(2010, 1, 1)
|
449
|
+
end
|
450
|
+
|
451
|
+
should "return two results" do
|
452
|
+
assert_equal 2, @s.all.size
|
453
|
+
end
|
454
|
+
|
455
|
+
should "not contain results with timestamp column before 2010" do
|
456
|
+
assert_equal [], @s.all.select {|r|
|
457
|
+
r.tms < Time.utc(2010, 1, 1)
|
458
|
+
}
|
459
|
+
end
|
289
460
|
end
|
290
|
-
|
291
|
-
|
292
|
-
|
461
|
+
|
462
|
+
context "where timestamp column is before the year 2010" do
|
463
|
+
setup do
|
464
|
+
@s.tms_lt = Time.utc(2010, 1, 1)
|
465
|
+
end
|
466
|
+
|
467
|
+
should "return seven results" do
|
468
|
+
assert_equal 7, @s.all.size
|
469
|
+
end
|
470
|
+
|
471
|
+
should "not contain results with timestamp in 2010" do
|
472
|
+
assert_equal [], @s.all.select {|r|
|
473
|
+
r.tms >= Time.utc(2010, 1, 1)
|
474
|
+
}
|
475
|
+
end
|
293
476
|
end
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
477
|
+
|
478
|
+
context "where decimal column is > 5000" do
|
479
|
+
setup do
|
480
|
+
@s.dec_gt = 5000
|
481
|
+
end
|
482
|
+
|
483
|
+
should "return four results" do
|
484
|
+
assert_equal 4, @s.all.size
|
485
|
+
end
|
486
|
+
|
487
|
+
should "not contain results with decimal column <= 5000" do
|
488
|
+
assert_equal [], @s.all.select {|r|
|
489
|
+
r.dec <= 5000
|
490
|
+
}
|
491
|
+
end
|
299
492
|
end
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
493
|
+
|
494
|
+
context "where float column is between 2.5 and 3.5" do
|
495
|
+
setup do
|
496
|
+
@s.flt_gte = 2.5
|
497
|
+
@s.flt_lte = 3.5
|
498
|
+
end
|
499
|
+
|
500
|
+
should "return three results" do
|
501
|
+
assert_equal 3, @s.all.size
|
502
|
+
end
|
503
|
+
|
504
|
+
should "not contain results with float column outside constraints" do
|
505
|
+
assert_equal [], @s.all.select {|r|
|
506
|
+
r.flt < 2.5 || r.flt > 3.5
|
507
|
+
}
|
508
|
+
end
|
306
509
|
end
|
307
|
-
|
308
|
-
|
309
|
-
|
510
|
+
|
511
|
+
context "where integer column is in the set (1, 8, 729)" do
|
512
|
+
setup do
|
513
|
+
@s.int_in = [1, 8, 729]
|
514
|
+
end
|
515
|
+
|
516
|
+
should "return three results" do
|
517
|
+
assert_equal 3, @s.all.size
|
518
|
+
end
|
519
|
+
|
520
|
+
should "not contain results outside the specified set" do
|
521
|
+
assert_equal [], @s.all.select {|r|
|
522
|
+
![1, 8, 729].include?(r.int)
|
523
|
+
}
|
524
|
+
end
|
310
525
|
end
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
526
|
+
|
527
|
+
context "where integer column is not in the set (1, 8, 729)" do
|
528
|
+
setup do
|
529
|
+
@s.int_not_in = [1, 8, 729]
|
530
|
+
end
|
531
|
+
|
532
|
+
should "return six results" do
|
533
|
+
assert_equal 6, @s.all.size
|
534
|
+
end
|
535
|
+
|
536
|
+
should "not contain results outside the specified set" do
|
537
|
+
assert_equal [], @s.all.reject {|r|
|
538
|
+
![1, 8, 729].include?(r.int)
|
539
|
+
}
|
540
|
+
end
|
316
541
|
end
|
317
542
|
end
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
should "return three results" do
|
325
|
-
assert_equal 3, @s.all.size
|
326
|
-
end
|
327
|
-
|
328
|
-
should "not contain results outside the specified set" do
|
329
|
-
assert_empty @s.all.select {|r|
|
330
|
-
![1, 8, 729].include?(r.int)
|
331
|
-
}
|
332
|
-
end
|
543
|
+
end
|
544
|
+
|
545
|
+
context_a_search_against "a relation with existing criteria and joins",
|
546
|
+
Company.where(:name => "Initech").joins(:developers) do
|
547
|
+
should "return the same results as a non-searched relation with no search terms" do
|
548
|
+
assert_equal Company.where(:name => "Initech").joins(:developers).all, @s.all
|
333
549
|
end
|
334
|
-
|
335
|
-
context "
|
550
|
+
|
551
|
+
context "with a search against the joined association's data" do
|
336
552
|
setup do
|
337
|
-
@s.
|
553
|
+
@s.developers_salary_less_than = 75000
|
338
554
|
end
|
339
|
-
|
340
|
-
should "
|
341
|
-
assert_equal
|
555
|
+
|
556
|
+
should "not ask to join the association twice" do
|
557
|
+
assert_equal 1, @s.relation.joins_values.size
|
342
558
|
end
|
343
|
-
|
344
|
-
should "
|
345
|
-
|
346
|
-
![1, 8, 729].include?(r.int)
|
347
|
-
}
|
559
|
+
|
560
|
+
should "return a filtered result set based on the criteria of the searched relation" do
|
561
|
+
assert_equal Company.where(:name => 'Initech').all, @s.all.uniq
|
348
562
|
end
|
349
563
|
end
|
350
564
|
end
|