meta_search 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|