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.
@@ -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