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.
@@ -57,7 +57,7 @@ module MetaSearch
57
57
  end
58
58
 
59
59
  def collapse_multiparameter_options(opts)
60
- opts.each_key do |k|
60
+ opts.keys.each do |k|
61
61
  if k.include?("(")
62
62
  real_attribute, position = k.split(/\(|\)/)
63
63
  cast = %w(a s i).include?(position.last) ? position.last : nil
@@ -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_ (alias: _ne_) - The opposite of equals, oddly enough.
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_ (alias: _ni_) - Like above, but negated.
17
+ # * _not_in_ (aliases: _ni_, _notin_) - Like above, but negated.
18
18
  #
19
19
  # === Strings
20
20
  #
21
- # * _contains_ (alias: _like_) - Substring match.
22
- # * _does_not_contain_ (alias: _nlike_) - Negative substring match.
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_ (alias: _gte_) - 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_ (alias: _lte_) - 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, :condition, :substitutions, :formatter
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
- @condition = where[:condition]
56
- @substitutions = where[:substitutions]
63
+ @predicate = where[:predicate]
64
+ @validator = where[:validator]
57
65
  @formatter = where[:formatter]
58
- @keep_arrays = where[:keep_arrays]
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
- def keep_arrays?
62
- @keep_arrays
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
- # Checks that the given +value+ is valid to use for the substitutions of this where.
66
- # Requires that there are the same number of parameters as substitutions, and none of'
67
- # them is blank.
68
- def valid_substitutions?(*values)
69
- values.flatten! unless values.size > 1 || self.keep_arrays?
70
- self.substitutions.count('?') == values.select {|v| !v.blank?}.size
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
- # MetaSearch::Where.add :between, :btw, {
79
- # :types => [:integer, :float, :decimal, :date, :datetime, :timestamp, :time],
80
- # :condition => 'BETWEEN',
81
- # :substitutions => '? AND ?',
82
- # :formatter => Proc.new {|param| param}
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>condition</tt> is the condition placed between the column and the substitutions, such as
94
- # BETWEEN, IN, or =. Default is =.
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, :condition => 'LIKE', :formatter => '"%#{param}%"'}]
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[:condition] ||= '='
126
- opts[:substitutions] ||= '?'
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
- # Returns the complete array of Wheres
149
- def all
150
- @@wheres
151
- end
152
-
153
- # Get the where matching a method or condition.
154
- def get(method_id_or_condition)
155
- return nil unless where_key = @@wheres.keys.
156
- sort {|a,b| b.length <=> a.length}.
157
- detect {|n| method_id_or_condition.to_s.match(/#{n}=?$/)}
158
- where = @@wheres[where_key]
159
- where = @@wheres[where] if where.is_a?(String)
160
- where
161
- end
162
-
163
- # Set the wheres to their default values, removing any customized settings.
164
- def initialize_wheres
165
- @@wheres = {}
166
- DEFAULT_WHERES.each do |where|
167
- add(*where)
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.3.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-03-16}
13
- s.description = %q{Adds a search method to your ActiveRecord models which returns an object to be used in form_for while constructing a search. Works with Rails 3 only.}
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/action_view.rb",
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.6}
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::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
75
- s.add_development_dependency(%q<activerecord>, [">= 3.0.0.beta"])
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<activerecord>, [">= 3.0.0.beta"])
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<activerecord>, [">= 3.0.0.beta"])
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
 
@@ -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
- metasearch_exclude_attr :updated_at
8
- metasearch_exclude_assoc :notes
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 %>
@@ -2,4 +2,7 @@ class Developer < ActiveRecord::Base
2
2
  belongs_to :company
3
3
  has_and_belongs_to_many :projects
4
4
  has_many :notes, :as => :notable
5
+
6
+ attr_searchable :name, :salary
7
+ assoc_searchable :notes, :projects, :company
5
8
  end
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
- context "A company search" do
6
- setup do
7
- @s = Company.search
8
- end
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
- end
37
-
38
- should "honor its associations' excluded attributes" do
39
- assert_nil @s.association_column(:data_types, :str)
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
- end
47
-
48
- context "where name contains optical" do
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 "return one result" do
54
- assert_equal 1, @s.all.size
20
+
21
+ should "have a column named name" do
22
+ assert @s.get_column(:name)
55
23
  end
56
-
57
- should "return a company named Advanced Optical Solutions" do
58
- assert_contains @s.all, Company.where(:name => 'Advanced Optical Solutions').first
24
+
25
+ should "exclude the column named updated_at" do
26
+ assert_nil @s.get_column(:updated_at)
59
27
  end
60
-
61
- should "not return a company named Initech" do
62
- assert_does_not_contain @s.all, Company.where(:name => "Initech").first
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
- end
65
-
66
- context "where developer name starts with Ernie" do
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 "return one result" do
72
- assert_equal 1, @s.all.size
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 "return a company named Mission Data" do
76
- assert_contains @s.all, Company.where(:name => 'Mission Data').first
44
+
45
+ should "honor its associations' excluded attributes" do
46
+ assert_nil @s.get_attribute(:data_types_str)
77
47
  end
78
-
79
- should "not return a company named Initech" do
80
- assert_does_not_contain @s.all, Company.where(:name => "Initech").first
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 "and slackers salary is greater than $70k" do
54
+
55
+ context "sorted by name in ascending order" do
84
56
  setup do
85
- @s.slackers_salary_gt = 70000
57
+ @s.meta_sort = 'name.asc'
86
58
  end
87
-
88
- should "return no results" do
89
- assert_equal 0, @s.all.size
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
- should "join developers twice" do
93
- assert @s.to_sql.match(/join "developers".*join "developers"/i)
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 "alias the second join of developers" do
97
- assert @s.to_sql.match(/join "developers" "slackers_companies"/i)
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
- end
101
-
102
- context "where developer note indicates he will crack yo skull" do
103
- setup do
104
- @s.developer_notes_note_equals = "Will show you what he's doing."
105
- end
106
-
107
- should "return one result" do
108
- assert_equal 1, @s.all.size
109
- end
110
-
111
- should "return a company named Advanced Optical Solutions" do
112
- assert_contains @s.all, Company.where(:name => 'Advanced Optical Solutions').first
113
- end
114
-
115
- should "not return a company named Mission Data" do
116
- assert_does_not_contain @s.all, Company.where(:name => "Mission Data").first
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
- end
139
-
140
- context "where name ends with Miller" do
141
- setup do
142
- @s.name_ends_with = 'Miller'
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
- should "return one result" do
146
- assert_equal 1, @s.all.size
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
- should "return a developer named Ernie Miller" do
150
- assert_contains @s.all, Developer.where(:name => 'Ernie Miller').first
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
- should "not return a developer named Herb Myers" do
154
- assert_does_not_contain @s.all, Developer.where(:name => "Herb Myers").first
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
- end
157
-
158
- context "where project estimated hours are greater than or equal to 1000" do
159
- setup do
160
- @s.projects_estimated_hours_gte = 1000
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
- should "return three results" do
164
- assert_equal 3, @s.all.size
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
- should "return these developers" do
168
- assert_same_elements @s.all.collect {|d| d.name},
169
- ['Peter Gibbons', 'Michael Bolton', 'Samir Nagheenanajar']
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
- end
172
-
173
- context "where project estimated hours are greater than 1000" do
174
- setup do
175
- @s.projects_estimated_hours_gt = 1000
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 no results" do
179
- assert_equal 0, @s.all.size
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
- context "A data type search" do
185
- setup do
186
- @s = DataType.search
187
- end
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 "contain no results with a false boolean column" do
205
- assert_does_not_contain @s.all.collect {|r| r.bln}, false
246
+
247
+ should "have an association named projects" do
248
+ assert @s.get_association(:projects)
206
249
  end
207
- end
208
-
209
- context "where date column is Christmas 2009 by array" do
210
- setup do
211
- @s.dat_equals = [2009, 12, 25]
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
- should "return one result" do
215
- assert_equal 1, @s.all.size
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
- should "contain a result with Christmas 2009 as its date" do
219
- assert_equal Date.parse('2009/12/25'), @s.first.dat
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
- end
222
-
223
- context "where date column is Christmas 2009 by Date object" do
224
- setup do
225
- @s.dat_equals = Date.new(2009, 12, 25)
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
- should "return one result" do
229
- assert_equal 1, @s.all.size
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
- should "contain a result with Christmas 2009 as its date" do
233
- assert_equal Date.parse('2009/12/25'), @s.first.dat
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
- end
236
-
237
- context "where time column is > 1:00 PM and < 3:30 PM" do
238
- setup do
239
- @s.tim_gt = Time.parse('2000-01-01 13:00') # Rails "dummy time" format
240
- @s.tim_lt = Time.parse('2000-01-01 15:30') # Rails "dummy time" format
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
- should "return three results" do
244
- assert_equal 3, @s.all.size
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
- should "not contain results with time column before or after constraints" do
248
- assert_empty @s.all.select {|r|
249
- r.tim < Time.parse('2000-01-01 13:00') || r.tim > Time.parse('2000-01-01 15:30')
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
- context "where timestamp column is in the year 2010" do
255
- setup do
256
- @s.tms_gte = Time.utc(2010, 1, 1)
257
- end
258
-
259
- should "return two results" do
260
- assert_equal 2, @s.all.size
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
- should "not contain results with timestamp column before 2010" do
264
- assert_empty @s.all.select {|r|
265
- r.tms < Time.utc(2010, 1, 1)
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
- end
269
-
270
- context "where datetime column is before the year 2010" do
271
- setup do
272
- @s.tms_lt = Time.utc(2010, 1, 1)
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
- should "return seven results" do
276
- assert_equal 7, @s.all.size
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
- should "not contain results with timestamp in 2010" do
280
- assert_empty @s.all.select {|r|
281
- r.tms >= Time.utc(2010, 1, 1)
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
- end
285
-
286
- context "where decimal column is > 5000" do
287
- setup do
288
- @s.dec_gt = 5000
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
- should "return four results" do
292
- assert_equal 4, @s.all.size
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
- should "not contain results with decimal column <= 5000" do
296
- assert_empty @s.all.select {|r|
297
- r.dec <= 5000
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
- end
301
-
302
- context "where float column is between 2.5 and 3.5" do
303
- setup do
304
- @s.flt_gte = 2.5
305
- @s.flt_lte = 3.5
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
- should "return three results" do
309
- assert_equal 3, @s.all.size
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
- should "not contain results with float column outside constraints" do
313
- assert_empty @s.all.select {|r|
314
- r.flt < 2.5 || r.flt > 3.5
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
- context "where integer column is in the set (1, 8, 729)" do
320
- setup do
321
- @s.int_in = [1, 8, 729]
322
- end
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 "where integer column is not in the set (1, 8, 729)" do
550
+
551
+ context "with a search against the joined association's data" do
336
552
  setup do
337
- @s.int_not_in = [1, 8, 729]
553
+ @s.developers_salary_less_than = 75000
338
554
  end
339
-
340
- should "return six results" do
341
- assert_equal 6, @s.all.size
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 "not contain results outside the specified set" do
345
- assert_empty @s.all.reject {|r|
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