Patterns with Valuable - the ModelSearch
By: Johnathon Wright on: August 24, 2012
I find that I write a lot of "advanced search" stuff, and that modeling an advanced search, and having that be in a separate class, is very good for my sanity.
h2. DataSearch model
The schema is below if you're interested. These models usually start in lib, and then move to app/search once I have four or five.
describe DataSearch do
it 'finds data' do
datum = Factory.create(:datum)
DataSearch.new.results.should include(datum)
end
end
So this is a "sanity check" test. I typically test what isn't included. But that could mean that I could get half-way through, write some poorly-thought-out condition that excludes everything, and then have to spend 30 minutes tracking it down. Instead, I just start by asserting that everything is included by default. In some cases, you want nothing to result if there are no conditions -- no problem, just write a test that does return some result.
``` class DataSearch < Valuable has_collection :codes
def results Datum.find(:all) end ```
h3. I want only certain fields
describe DataSearch do it 'filters by code' do datum = FactoryGirl.create(:datum, :code => 'hr') DataSearch.new(:codes => ['midi-chlorians']).results.should_not include(datum) end
end
and then
class DataSearch < Valuable
has_collection :codes
def condition_params { :codes => codes } end
def conditions out = [] out << 'code IN (:codes)' if codes.not.empty? out.join(' AND ') end
def results Datum.find(:all, :conditions => [conditions, condition_params]) end
end
h3. Filter by visit
describe DataSearch do it 'filters by visit' do one = FactoryGirl.create(:order) two = FactoryGirl.create(:order)
doppleganger = FactoryGirl.create(:datum, :order => one)
DataSearch.new(:visit_id => two.visit_id).results.should_not include(doppleganger)
end
end
and then
class DataSearch < Valuable
hascollection :codes hasvalue :visit_id, :klass => :integer
def conditionparams { :codes => codes, :visitid => visit_id } end
def conditions out = [] out << 'code IN (:codes)' if codes.not.empty? out << 'orders.visitid = :visitid' if visit_id out.join(' AND ') end
def joins out = [] out << 'LEFT OUTER JOIN orders ON data.orderid = orders.id' if visitid out.join(' ') unless out.empty? end
def results Datum.find(:all, :conditions => [conditions, condition_params], :joins => joins, :group => 'data.id') end
end
h2. The Schema
h3. Timepoint
A patient at a point in time. * patient_id * date * physician
h3. Order
a Physician orders some bloodwork or some other test. * timepointid * panelname
h3. Datum
- order_id
- code
- value
- units
Datum.validatesuniquenessof :code, :scope => :order_id